<!DOCTYPE html>
<html lang=zh>
<head>
    <meta charset="utf-8">
    
    <title>LErry</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="记录生活点滴">
<meta property="og:type" content="website">
<meta property="og:title" content="LErry">
<meta property="og:url" content="https://www.itchina.top/page/6/index.html">
<meta property="og:site_name" content="LErry">
<meta property="og:description" content="记录生活点滴">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="LErry">
<meta name="twitter:description" content="记录生活点滴">
    

    
        <link rel="alternate" href="/atom.xml" title="LErry" type="application/atom+xml" />
    

    
        <link rel="icon" href="/css/images/shortcut_icon.png" />
    

    <link rel="stylesheet" href="/libs/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/libs/open-sans/styles.css">
    <link rel="stylesheet" href="/libs/source-code-pro/styles.css">

    <link rel="stylesheet" href="/css/style.css">

    <script src="/libs/jquery/2.1.3/jquery.min.js"></script>
    
    
        <link rel="stylesheet" href="/libs/lightgallery/css/lightgallery.min.css">
    
    
        <link rel="stylesheet" href="/libs/justified-gallery/justifiedGallery.min.css">
    
    
    
    


</head>

<body>
    <div id="container">
        <header id="header">
    <div id="header-main" class="header-inner">
        <div class="outer">
            <a href="/" id="logo">
                <i class="logo"></i>
                <span class="site-title">LErry</span>
            </a>
            <nav id="main-nav">
                
                    <a class="main-nav-link" href="/.">主页</a>
                
                    <a class="main-nav-link" href="/freebooks">书籍</a>
                
                    <a class="main-nav-link" href="/tags">标签</a>
                
                    <a class="main-nav-link" href="/archives">归档</a>
                
                    <a class="main-nav-link" href="/aboutme">关于</a>
                
            </nav>
            
                
                <nav id="sub-nav">
                    <div class="profile" id="profile-nav">
                        <a id="profile-anchor" href="javascript:;">
                            <img class="avatar" src="/css/images/avatar.png" />
                            <i class="fa fa-caret-down"></i>
                        </a>
                    </div>
                </nav>
            
            <div id="search-form-wrap">

    <form class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
        <button type="submit" class="search-form-submit"></button>
    </form>
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="想要查找什么..." />
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<script src="/js/insight.js"></script>

</div>
        </div>
    </div>
    <div id="main-nav-mobile" class="header-sub header-inner">
        <table class="menu outer">
            <tr>
                
                    <td><a class="main-nav-link" href="/.">主页</a></td>
                
                    <td><a class="main-nav-link" href="/freebooks">书籍</a></td>
                
                    <td><a class="main-nav-link" href="/tags">标签</a></td>
                
                    <td><a class="main-nav-link" href="/archives">归档</a></td>
                
                    <td><a class="main-nav-link" href="/aboutme">关于</a></td>
                
                <td>
                    
    <div class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
    </div>

                </td>
            </tr>
        </table>
    </div>
</header>

        <div class="outer">
            
                

<aside id="profile">
    <div class="inner profile-inner">
        <div class="base-info profile-block">
            <img id="avatar" src="/css/images/avatar.png" />
            <h2 id="name">LErry Li</h2>
            <h3 id="title">知我者谓我心忧，不知我者谓我何求</h3>
            <span id="location"><i class="fa fa-map-marker"></i>Shanghai, China</span>
            <a id="follow" target="_blank" href="https://github.com/lerry903">关注我</a>
        </div>
        <div class="article-info profile-block">
            <div class="article-info-block">
                65
                <span>文章</span>
            </div>
            <div class="article-info-block">
                54
                <span>标签</span>
            </div>
        </div>
        
        <div class="profile-block social-links">
            <table>
                <tr>
                    
                    
                    <td>
                        <a href="https://github.com/lerry903" target="_blank" title="github" class=tooltip>
                            <i class="fa fa-github"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="mailto:lerryli@foxmail.com" target="_blank" title="envelope" class=tooltip>
                            <i class="fa fa-envelope"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="http://wpa.qq.com/msgrd?v=3&uin=824444270&site=qq&menu=yes" target="_blank" title="qq" class=tooltip>
                            <i class="fa fa-qq"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="https://weibo.com/5941010376" target="_blank" title="weibo" class=tooltip>
                            <i class="fa fa-weibo"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/atom.xml" target="_blank" title="rss" class=tooltip>
                            <i class="fa fa-rss"></i>
                        </a>
                    </td>
                    
                </tr>
            </table>
        </div>
        
    </div>
</aside>

            
            <section id="main">
    <article id="post-ActiveMQ笔记(6)：消息延时投递" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(6)：消息延时投递/">ActiveMQ笔记(6)：消息延时投递</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(6)：消息延时投递/">
            <time datetime="2018-04-19T16:34:21.413Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>在开发业务系统时，某些业务场景需要消息定时发送或延时发送（类似：飞信的短信定时发送需求），这时候就需要用到activemq的消息延时投递，详细的文档可参考<br><a href="http://activemq.apache.org/delay-and-schedule-message-delivery.html" target="_blank" rel="noopener"> 官网说明 </a><br>，本文只介绍二种常用的用法：</p>
<p>注：本文采用spring的JmsTemplate来发送消息</p>
<p>步骤1、  首先要修改activemq.xml配置文件，启用延时投递</p>
<pre><code>1 &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; ... schedulerSupport=&quot;true&quot; &gt;
2     ...
3   &lt;/broker&gt;
</code></pre><p>即：在broker节点加上schedulerSupport=”true”，然后重启activemq即可</p>
<p>步骤2、  定义一个MessagePostProcessor的实现类</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>38</p>
<p><code>import</code> <code>javax.jms.JMSException;</code></p>
<p><code>import</code> <code>javax.jms.Message;</code></p>
<p><code>import</code> <code>lombok.Data;</code></p>
<p><code>import</code> <code>org.apache.activemq.ScheduledMessage;</code></p>
<p><code>import</code> <code>org.apache.commons.lang3.StringUtils;</code></p>
<p><code>import</code> <code>org.springframework.jms.core.MessagePostProcessor;</code></p>
<p><code>/**</code></p>
<p><code></code> <code>* MQ延时投递处理器（注：ActiveMQ的配置文件中，要配置schedulerSupport=&quot;true&quot;，否则不起作用）</code></p>
<p><code></code> <code>* by: 杨俊明 2016-06-16</code></p>
<p><code></code> <code>*/</code></p>
<p><code>@Data</code></p>
<p><code>public</code> <code>class</code> <code>ScheduleMessagePostProcessor</code> <code>implements</code> <code>MessagePostProcessor {</code></p>
<p><code></code> <code>private</code> <code>long</code> <code>delay = 0l;</code></p>
<p><code></code> <code>private</code> <code>String corn =</code> <code>null</code> <code>;</code></p>
<p><code></code> <code>public</code> <code>ScheduleMessagePostProcessor(</code> <code>long</code> <code>delay) {</code></p>
<p><code></code> <code>this</code> <code>.delay = delay;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>ScheduleMessagePostProcessor(String cron) {</code></p>
<p><code></code> <code>this</code> <code>.corn = cron;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>Message postProcessMessage(Message message)</code> <code>throws</code> <code>JMSException {</code></p>
<p><code></code> <code>if</code> <code>(delay &gt;</code> <code>0</code> <code>) {</code></p>
<p><code></code> <code>message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>if</code> <code>(!StringUtils.isEmpty(corn)) {</code></p>
<p><code></code> <code>message.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_CRON, corn);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>return</code> <code>message;</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>步骤3、  jmsTemplate发送示例</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p><code>Object message1 =</code> <code>&quot;corn消息内容：&quot;</code> <code>\+ DateUtil.formatDate(</code> <code>new</code> <code>Date());</code></p>
<p><code>//分 时 天 月 星期几</code></p>
<p><code>jmsTemplate.convertAndSend(message1,</code> <code>new</code> <code>ScheduleMessagePostProcessor(</code> <code>&quot;40 22 * * *&quot;</code> <code>));</code></p>
<p><code>logger.info(</code> <code>&quot;消息1：[&quot;</code> <code>\+ message1 +</code> <code>&quot;] 延时发送成功！&quot;</code> <code>);</code></p>
<p><code>jmsTemplate.convertAndSend(message1,</code> <code>new</code> <code>ScheduleMessagePostProcessor(</code> <code>&quot;50 22 * * *&quot;</code> <code>));</code></p>
<p><code>logger.info(</code> <code>&quot;消息1：[&quot;</code> <code>\+ message1 +</code> <code>&quot;] 延时发送成功！&quot;</code> <code>);</code></p>
<p><code>Object message2 =</code> <code>&quot;message：&quot;</code> <code>\+ DateUtil.formatDate(</code> <code>new</code> <code>Date());</code></p>
<p><code>jmsTemplate.convertAndSend(message2,</code> <code>new</code> <code>ScheduleMessagePostProcessor(</code> <code>30</code> <code>*</code> <code>1000</code> <code>));</code> <code>//延时30秒</code></p>
<p><code>jmsTemplate.convertAndSend(message2,</code> <code>new</code> <code>ScheduleMessagePostProcessor(</code> <code>3600</code> <code>*</code> <code>24</code> <code>*</code> <code>1000</code> <code>));</code> <code>//延时24小时</code></p>
<p><code>logger.info(</code> <code>&quot;消息2：[&quot;</code> <code>\+ message2 +</code> <code>&quot;] 延时发送成功！&quot;</code> <code>);</code></p>
<p>上面的代码演示了二种延时的用法：延时N毫秒、按corn表达式延时（注：此corn表达式并非Quartz框架中的corn表达式，而是linux中corntab<br>中的表达 式，基本顺序是”分(0-59) 时(0-23) 日(1-31) 月（1-12） 星期几(1-7) “）</p>
<p>发送成功后，可以登录activemq的webconsole查看消息的属性：</p>
<p>在scheduled面板中，可以看到延时的消息</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201606/27612-201606182302114
95-737992865.png" alt="点击看大图"></p>
<p>注：在开启消息持久化存储的前提下，就算把相应的queue在webconsole面板中删除（即删除队列），只要投递的时间尚未到，该消息也不会删除，仍然能正常延<br>时投递。</p>
<p>此外，在queues面板中，如何查看某条具体的消息，也可以通过属性发现这条消息是延时消息，参考下图：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201606/27612-201606182308471
04-233535248.png" alt="点击看大图"></p>
<p>参考文章：<br>1、 <a href="http://activemq.apache.org/delay-
and-schedule-message-delivery.html" target="_blank" rel="noopener"> Delay and Schedule Message Delivery </a></p>
<p>2、 <a href="https://zh.wikipedia.org/wiki/Cron" target="_blank" rel="noopener"> 喂鸡百科上的Corn表达式解释 </a> （中文）</p>
<p>3、 <a href="https://en.wikipedia.org/wiki/Cron" target="_blank" rel="noopener"> 喂鸡百科上的Corn表达式解释 </a> （英文）</p>
<p><a href="http://activemq.apache.org/kahadb.html" target="_blank" rel="noopener"> 4、kahaDB官方文档 </a></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(6)：消息延时投递/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(6)：消息延时投递/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(7)：如何清理无效的延时消息？" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(7)：如何清理无效的延时消息？/">ActiveMQ笔记(7)：如何清理无效的延时消息？</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(7)：如何清理无效的延时消息？/">
            <time datetime="2018-04-19T16:34:21.413Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>ActiveMQ的延时消息是一个让人又爱又恨的功能，具体使用可参考上篇 <a href="http://blog.csdn.net/lsy0903/article/details/56672475" target="_blank" rel="noopener"> ActiveMQ笔记(6)：消息延时投递
</a> ，在很多需要消息延时投递的业务场景十分有用<br>，但是也有一个缺陷，在一些大访问量的场景，如果瞬间向MQ发送海量的延时消息，超过MQ的调度能力，就会造成很多消息到了该投递的时刻，却没有投递出去，形成积压，<br>一直停留在ActiveMQ web控制台的Scheduled面板中。</p>
<p>下面的代码演示了，如何清理activemq中的延时消息（包括：全部清空及清空指定时间段的延时消息），这也是目前唯一可行的办法。</p>
<p>为了演示方便，先封装一个小工具类：</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>38</p>
<p>39</p>
<p>40</p>
<p>41</p>
<p>42</p>
<p>43</p>
<p>44</p>
<p>45</p>
<p>46</p>
<p>47</p>
<p>48</p>
<p>49</p>
<p>50</p>
<p>51</p>
<p>52</p>
<p>53</p>
<p>54</p>
<p>55</p>
<p>56</p>
<p>57</p>
<p>58</p>
<p>59</p>
<p>60</p>
<p>61</p>
<p>62</p>
<p>63</p>
<p>64</p>
<p>65</p>
<p>66</p>
<p>67</p>
<p>68</p>
<p>69</p>
<p>70</p>
<p>71</p>
<p>72</p>
<p>73</p>
<p>74</p>
<p>75</p>
<p>76</p>
<p>77</p>
<p>78</p>
<p>79</p>
<p>80</p>
<p>81</p>
<p>82</p>
<p>83</p>
<p>84</p>
<p>85</p>
<p>86</p>
<p>87</p>
<p>88</p>
<p>89</p>
<p>90</p>
<p>91</p>
<p>92</p>
<p>93</p>
<p>94</p>
<p>95</p>
<p>96</p>
<p>97</p>
<p>98</p>
<p>99</p>
<p>100</p>
<p>101</p>
<p>102</p>
<p>103</p>
<p>104</p>
<p>105</p>
<p>106</p>
<p>107</p>
<p>108</p>
<p>109</p>
<p>110</p>
<p>111</p>
<p>112</p>
<p>113</p>
<p>114</p>
<p>115</p>
<p>116</p>
<p>117</p>
<p>118</p>
<p>119</p>
<p>120</p>
<p>121</p>
<p><code>package</code> <code>cn.mwee.utils.mq;</code></p>
<p><code>import</code> <code>cn.mwee.utils.list.ListUtil;</code></p>
<p><code>import</code> <code>cn.mwee.utils.log4j2.MwLogger;</code></p>
<p><code>import</code> <code>org.apache.commons.lang.StringUtils;</code></p>
<p><code>import</code> <code>org.slf4j.Logger;</code></p>
<p><code>import</code> <code>org.springframework.jms.core.JmsTemplate;</code></p>
<p><code>import</code> <code>org.springframework.jms.core.MessagePostProcessor;</code></p>
<p><code>import</code> <code>javax.jms.ConnectionFactory;</code></p>
<p><code>import</code> <code>javax.jms.JMSException;</code></p>
<p><code>import</code> <code>javax.jms.TextMessage;</code></p>
<p><code>import</code> <code>java.util.ArrayList;</code></p>
<p><code>import</code> <code>java.util.List;</code></p>
<p><code>import</code> <code>java.util.stream.Collectors;</code></p>
<p><code>/**</code></p>
<p><code></code> <code>* Created by yangjunming on 6/20/16.</code></p>
<p><code></code> <code>*/</code></p>
<p><code>public</code> <code>final</code> <code>class</code> <code>MessageUtil {</code></p>
<p><code></code> <code>private</code> <code>Logger logger =</code> <code>new</code> <code>MwLogger(MessageUtil.</code> <code>class</code> <code>);</code> <code>//这里就是一个Log4j2的实例,大家可以换成原生的log4j2或类似工具</code></p>
<p><code></code> <code>private</code> <code>ConnectionFactory connectionFactory;</code></p>
<p><code></code> <code>private</code> <code>long</code> <code>receiveTimeout;</code> <code>//接收超时时间</code></p>
<p><code></code> <code>private</code> <code>JmsTemplate jmsTemplate;</code></p>
<p><code></code> <code>private</code> <code>List&lt;String&gt; destinationQueueNames;</code></p>
<p><code></code> <code>private</code> <code>final</code> <code>static</code> <code>String BACKUP_QUEUE_SUFFIX =</code> <code>&quot;_B&quot;</code><br><code>;</code></p>
<p><code></code> <code>private</code> <code>boolean</code> <code>autoBackup =</code> <code>false</code> <code>;</code> <code>//是否自动将消息备份到_b的队列，方便调试</code></p>
<p><code></code> <code>public</code> <code>MessageUtil(</code> <code>final</code> <code>ConnectionFactory
connectionFactory,</code> <code>final</code> <code>long</code> <code>receiveTimeout,</code> <code>final</code> <code>List&lt;String&gt; destinationQueueNames) {</code></p>
<p><code></code> <code>this</code> <code>.connectionFactory = connectionFactory;</code></p>
<p><code></code> <code>this</code> <code>.receiveTimeout = receiveTimeout;</code></p>
<p><code></code> <code>this</code> <code>.destinationQueueNames =</code> <code>new</code> <code>ArrayList&lt;&gt;();</code></p>
<p><code></code> <code>this</code> <code>.destinationQueueNames.addAll(destinationQueueNames.stream().co
llect(Collectors.toList()));</code></p>
<p><code></code> <code>jmsTemplate =</code> <code>new</code> <code>JmsTemplate(</code> <code>this</code> <code>.connectionFactory);</code></p>
<p><code></code> <code>jmsTemplate.setReceiveTimeout(</code> <code>this</code> <code>.receiveTimeout);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>MessageUtil(ConnectionFactory connectionFactory, List&lt;String&gt;
destinationQueueNames) {</code></p>
<p><code></code> <code>this</code> <code>(connectionFactory,</code> <code>10000</code> <code>, destinationQueueNames);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>convertAndSend(Object message) {</code></p>
<p><code></code> <code>if</code> <code>(ListUtil.isEmpty(destinationQueueNames)) {</code></p>
<p><code></code> <code>logger.error(</code> <code>&quot;目标队列为空，无法发送，请检查配置！message =&gt; &quot;</code> <code>\+
message.toString());</code></p>
<p><code></code> <code>return</code> <code>;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>for</code> <code>(String dest : destinationQueueNames) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(dest, message);</code></p>
<p><code></code> <code>if</code> <code>(autoBackup) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(dest + BACKUP_QUEUE_SUFFIX, message);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>convertAndSend(Object message, MessagePostProcessor
messagePostProcessor) {</code></p>
<p><code></code> <code>if</code> <code>(ListUtil.isEmpty(destinationQueueNames)) {</code></p>
<p><code></code> <code>logger.error(</code> <code>&quot;目标队列为空，无法发送，请检查配置！message =&gt; &quot;</code> <code>\+
message.toString());</code></p>
<p><code></code> <code>return</code> <code>;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>for</code> <code>(String dest : destinationQueueNames) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(dest, message, messagePostProcessor);</code></p>
<p><code></code> <code>if</code> <code>(autoBackup) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(dest + BACKUP_QUEUE_SUFFIX, message,
messagePostProcessor);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>convertAndSend(String destinationName, Object
message) {</code></p>
<p><code></code> <code>if</code> <code>(StringUtils.isBlank(destinationName)) {</code></p>
<p><code></code> <code>logger.error(</code> <code>&quot;目标队列为空，无法发送，请检查配置！message =&gt; &quot;</code> <code>\+
message.toString());</code></p>
<p><code></code> <code>return</code> <code>;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(destinationName, message);</code></p>
<p><code></code> <code>if</code> <code>(autoBackup) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(destinationName + BACKUP_QUEUE_SUFFIX,
message);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>convertAndSend(String destinationName, Object
message, MessagePostProcessor messagePostProcessor) {</code></p>
<p><code></code> <code>if</code> <code>(StringUtils.isBlank(destinationName)) {</code></p>
<p><code></code> <code>logger.error(</code> <code>&quot;目标队列为空，无法发送，请检查配置！message =&gt; &quot;</code> <code>\+
message.toString());</code></p>
<p><code></code> <code>return</code> <code>;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(destinationName, message,
messagePostProcessor);</code></p>
<p><code></code> <code>if</code> <code>(autoBackup) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(destinationName + BACKUP_QUEUE_SUFFIX,
message, messagePostProcessor);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>static</code> <code>String getText(javax.jms.Message message) {</code></p>
<p><code></code> <code>if</code> <code>(message</code> <code>instanceof</code> <code>TextMessage) {</code></p>
<p><code></code> <code>try</code> <code>{</code></p>
<p><code></code> <code>return</code> <code>((TextMessage) message).getText();</code></p>
<p><code></code> <code>}</code> <code>catch</code> <code>(JMSException e) {</code></p>
<p><code></code> <code>return</code> <code>message.toString();</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>return</code> <code>message.toString();</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>String getFirstDestination() {</code></p>
<p><code></code> <code>if</code> <code>(ListUtil.isEmpty(destinationQueueNames)) {</code></p>
<p><code></code> <code>return</code> <code>null</code> <code>;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>return</code> <code>destinationQueueNames.get(</code> <code>0</code> <code>);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>boolean</code> <code>isAutoBackup() {</code></p>
<p><code></code> <code>return</code> <code>autoBackup;</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>setAutoBackup(</code> <code>boolean</code> <code>autoBackup) {</code></p>
<p><code></code> <code>this</code> <code>.autoBackup = autoBackup;</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>其中主要就用到了convertAndSend(Object message, MessagePostProcessor<br>messagePostProcessor) 这个方法，其它代码可以无视。</p>
<p>先来模拟瞬间向MQ发送大量延时消息：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p><code>/**</code></p>
<p><code></code> <code>* 发送延时消息</code></p>
<p><code></code> <code>*</code></p>
<p><code></code> <code>* @param messageUtil</code></p>
<p><code></code> <code>*/</code></p>
<p><code>private</code> <code>static</code> <code>void</code> <code>sendScheduleMessage(MessageUtil messageUtil)
{</code></p>
<p><code></code> <code>for</code> <code>(</code> <code>int</code> <code>i =</code> <code>0</code> <code>; i &lt;</code> <code>10000</code> <code>; i++) {</code></p>
<p><code></code> <code>Object obj =</code> <code>&quot;test:&quot;</code> <code>\+ i;</code></p>
<p><code></code> <code>messageUtil.convertAndSend(obj,</code> <code>new</code> <code>ScheduleMessagePostProcessor(</code> <code>1000</code> <code>\+ i *</code> <code>1000</code> <code>));</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>这里向MQ发送了1w条延时消息，每条消息延时1秒*i，上面代码中的ScheduleMessagePostProcessor类可在上篇中找到。</p>
<p>运行完之后，MQ中应该堆积着了很多消息了：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201609/27612-20160904201313552-64
4393418.png" alt=""></p>
<p>下面的代码可以清空所有延时消息：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p><code>/**</code></p>
<p><code></code> <code>* 删除所有延时消息</code></p>
<p><code></code> <code>*</code></p>
<p><code></code> <code>* @param connectionFactory</code></p>
<p><code></code> <code>* @throws JMSException</code></p>
<p><code></code> <code>*/</code></p>
<p><code>private</code> <code>static</code> <code>void</code> <code>deleteAllScheduleMessage(</code> <code>final</code> <code>ConnectionFactory connectionFactory)</code> <code>throws</code> <code>JMSException {</code></p>
<p><code></code> <code>Connection conn = connectionFactory.createConnection();</code></p>
<p><code></code> <code>Session session = conn.createSession(</code> <code>false</code> <code>,
Session.AUTO_ACKNOWLEDGE);</code></p>
<p><code></code> <code>Destination management =
session.createTopic(ScheduledMessage.AMQ_SCHEDULER_MANAGEMENT_DESTINATION);</code></p>
<p><code></code> <code>MessageProducer producer = session.createProducer(management);</code></p>
<p><code></code> <code>Message request = session.createMessage();</code></p>
<p><code></code> <code>request.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION,
ScheduledMessage.AMQ_SCHEDULER_ACTION_REMOVEALL);</code></p>
<p><code></code> <code>producer.send(request);</code></p>
<p><code>}</code></p>
<p>清空所有延时消息，有些用力过猛了，很多时候，我们只需要清理掉过期的延时消息（即：本来计划是8:00投递出去的消息，结果过了8点还没投递出去）</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p><code>/**</code></p>
<p><code></code> <code>* 删除过期的延时消息</code></p>
<p><code></code> <code>*</code></p>
<p><code></code> <code>* @param connectionFactory</code></p>
<p><code></code> <code>* @throws JMSException</code></p>
<p><code></code> <code>*/</code></p>
<p><code>private</code> <code>static</code> <code>void</code> <code>deleteExpiredScheduleMessage(</code> <code>final</code> <code>ConnectionFactory connectionFactory)</code> <code>throws</code> <code>JMSException {</code></p>
<p><code></code> <code>long</code> <code>start = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(</code><br><code>12</code> <code>);</code> <code>//删除：当前时间前12小时范围的延时消息</code></p>
<p><code></code> <code>long</code> <code>end = System.currentTimeMillis();</code></p>
<p><code></code> <code>Connection conn = connectionFactory.createConnection();</code></p>
<p><code></code> <code>Session session = conn.createSession(</code> <code>false</code> <code>,
Session.AUTO_ACKNOWLEDGE);</code></p>
<p><code></code> <code>Destination management =
session.createTopic(ScheduledMessage.AMQ_SCHEDULER_MANAGEMENT_DESTINATION);</code></p>
<p><code></code> <code>MessageProducer producer = session.createProducer(management);</code></p>
<p><code></code> <code>Message request = session.createMessage();</code></p>
<p><code></code> <code>request.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION,
ScheduledMessage.AMQ_SCHEDULER_ACTION_REMOVEALL);</code></p>
<p><code></code> <code>request.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION_START_TIME,
Long.toString(start));</code></p>
<p><code></code> <code>request.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION_END_TIME,
Long.toString(end));</code></p>
<p><code></code> <code>producer.send(request);</code></p>
<p><code>}</code></p>
<p>与上一段代码基本相似，只是多指定了删除消息的起止时间段。</p>
<p>最后贴一段spring的配置文件及main函数入口</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<pre><code> 1 &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
 2 &lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3        xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4        xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;
 5 
 6     &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
 7         &lt;property name=&quot;connectionFactory&quot;&gt;
 8             &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
 9                 &lt;property name=&quot;brokerURL&quot;
10                           value=&quot;failover:(tcp://localhost:61616,tcp://localhost:61626)?randomize=false&amp;amp;backup=true&quot;/&gt;
11                 &lt;property name=&quot;maxThreadPoolSize&quot; value=&quot;100&quot;/&gt;
12             &lt;/bean&gt;
13         &lt;/property&gt;
14     &lt;/bean&gt;
15 
16     &lt;bean id=&quot;messageUtil&quot; class=&quot;cn.mwee.utils.mq.MessageUtil&quot;&gt;
17         &lt;constructor-arg index=&quot;0&quot; ref=&quot;jmsFactory&quot;/&gt;
18         &lt;constructor-arg index=&quot;1&quot; value=&quot;10000&quot;/&gt;
19         &lt;constructor-arg index=&quot;2&quot;&gt;
20             &lt;list&gt;
21                 &lt;value&gt;dest1&lt;/value&gt;
22                 &lt;value&gt;dest2&lt;/value&gt;
23             &lt;/list&gt;
24         &lt;/constructor-arg&gt;
25         &lt;property name=&quot;autoBackup&quot; value=&quot;true&quot;/&gt;
26     &lt;/bean&gt;
27 
28 &lt;/beans&gt;
</code></pre><p>main函数：</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p><code></code> <code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code> <code>throws</code> <code>InterruptedException, JMSException {</code></p>
<p><code></code> <code>ApplicationContext context =</code> <code>new</code> <code>ClassPathXmlApplicationContext(</code> <code>&quot;spring-sender.xml&quot;</code> <code>);</code></p>
<p><code></code> <code>ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.</code> <code>class</code> <code>,</code> <code>&quot;jmsFactory&quot;</code> <code>);</code></p>
<p><code></code> <code>MessageUtil messageUtil = context.getBean(MessageUtil.</code> <code>class</code> <code>);</code></p>
<p><code>//        sendScheduleMessage(messageUtil);</code></p>
<p><code>//        deleteAllScheduleMessage(connectionFactory);</code></p>
<p><code></code> <code>deleteExpiredScheduleMessage(connectionFactory);</code></p>
<p><code></code> <code>}</code></p>
<p>参考文章：</p>
<p><a href="http://timbish.blogspot.com/2010/10
/enhanced-jms-scheduler-in-activemq.html" target="_blank" rel="noopener"> Enhanced JMS Scheduler in ActiveMQ </a></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(7)：如何清理无效的延时消息？/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(7)：如何清理无效的延时消息？/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(5)：JMX监控" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(5)：JMX监控/">ActiveMQ笔记(5)：JMX监控</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(5)：JMX监控/">
            <time datetime="2018-04-19T16:34:21.412Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>, <a class="tag-link" href="/tags/jmx/">jmx</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>系统上线运行后，及时监控报警是很必要的手段，对于ActiveMQ而言，主要监控的指标有:MQ本身的健康状况、每个队列的生产者数量、消费者数量、队列的当前消息<br>数等。</p>
<p>ActiveMQ支持JMX监控，使用步骤如下：</p>
<p>一、  修改conf/activemq.xml</p>
<pre><code>&lt;broker … useJmx=&quot;true”&gt;
    &lt;managementContext&gt;
        &lt;managementContext createConnector=&quot;true&quot; connectorPort=“jmx端口号” connectorHost=“本机ip地址&quot; /&gt;         
    &lt;/managementContext&gt;
&lt;/broker&gt;
</code></pre><p>二、  设置jmx.access、jmx.password的文件权限</p>
<p>?</p>
<p>1</p>
<p><code>chmod</code> <code>400 conf</code> <code>/jmx</code> <code>.*</code></p>
<p>(即：将jmx.password, jmx.access这二个文件设置成只读权限，activemq出于安全考虑，要求这二个文件只读)</p>
<p>三、  修改bin\activemq 启动shell脚本</p>
<p>找到invoke_start(){ 这段，然后在前面插入：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p><code>ACTIVEMQ_CONF=“jmx.password所在位置的物理路目录&quot;</code></p>
<p><code>ACTIVEMQ_SUNJMX_START=</code> <code>&quot;-Dcom.sun.management.jmxremote.port=端口号 &quot;</code></p>
<p><code>ACTIVEMQ_SUNJMX_START=</code> <code>&quot;$ACTIVEMQ_SUNJMX_START
-Dcom.sun.management.jmxremote.password.file=${ACTIVEMQ_CONF}/jmx.password&quot;</code></p>
<p><code>ACTIVEMQ_SUNJMX_START=</code> <code>&quot;$ACTIVEMQ_SUNJMX_START
-Dcom.sun.management.jmxremote.access.file=${ACTIVEMQ_CONF}/jmx.access&quot;</code></p>
<p><code>ACTIVEMQ_SUNJMX_START=</code> <code>&quot;$ACTIVEMQ_SUNJMX_START
-Dcom.sun.management.jmxremote.ssl=false&quot;</code></p>
<p>然后重启activemq即可。</p>
<p>然后在jconsole中，可以输入 ip地址:jmx端口号</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201605/27612-20160526201918584-13
6169982.png" alt=""></p>
<p>其中username,password即jmx.password中定义的用户名和密码。</p>
<p>四、  spring中使用JMX</p>
<p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<pre><code>&lt;bean class=&quot;org.springframework.jmx.support.MBeanServerConnectionFactoryBean&quot; id=&quot;mbeanServerConnection1&quot;&gt;
    &lt;property name=&quot;serviceUrl&quot; value=&quot;${mq_jmx_url1}&quot;/&gt;
    &lt;property name=&quot;connectOnStartup&quot; value=&quot;false&quot;/&gt;
    &lt;property name=&quot;environment&quot;&gt;
        &lt;props&gt;
            &lt;prop key=&quot;java.naming.security.principal&quot;&gt;
                ${mq_jmx_user1}
            &lt;/prop&gt;
            &lt;prop key=&quot;java.naming.security.credentials&quot;&gt;
                ${mq_jmx_passwor1}
            &lt;/prop&gt;
        &lt;/props&gt;
    &lt;/property&gt;
&lt;/bean&gt;
</code></pre><p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<p>其中serviceUrl的值类似：service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi</p>
<p>参考示例：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p><code>private List&lt;ActiveMQData&gt; getMonitorDataList(MBeanServerConnection conn,
String objectName) {</code></p>
<p><code></code> <code>List&lt;ActiveMQData&gt; datas = new ArrayList&lt;&gt;();</code></p>
<p><code></code> <code>try {</code></p>
<p><code></code> <code>ObjectName objRootName = new ObjectName(objectName);</code></p>
<p><code></code> <code>String brokerName = (String) conn.getAttribute(objRootName,</code> <code>&quot;BrokerName&quot;</code> <code>);</code></p>
<p><code></code> <code>String brokerId = (String) conn.getAttribute(objRootName,</code> <code>&quot;BrokerId&quot;</code> <code>);</code></p>
<p><code></code> <code>String openWireUrl = (String) conn.getAttribute(objRootName,</code> <code>&quot;OpenWireURL&quot;</code> <code>);</code></p>
<p><code></code> <code>//</code> <code>健康状态</code></p>
<p><code></code> <code>ObjectName healthObjName = new ObjectName(objectName +</code> <code>&quot;,service=Health&quot;</code> <code>);</code></p>
<p><code></code> <code>String healthStatus = (String) conn.getAttribute(healthObjName,</code> <code>&quot;CurrentStatus&quot;</code> <code>);</code></p>
<p><code></code> <code>//</code> <code>遍历队列</code></p>
<p><code></code> <code>ObjectName[] objectNames = (ObjectName[]) conn.getAttribute(objRootName,</code> <code>&quot;Queues&quot;</code> <code>);</code></p>
<p><code></code> <code>Arrays.</code> <code>sort</code> <code>(objectNames);</code></p>
<p><code></code> <code>List&lt;String&gt; blackList = monitorConfig.getQueueBlackList();</code></p>
<p><code></code> <code>for</code> <code>(ObjectName queueName : objectNames) {</code></p>
<p><code></code> <code>...</code></p>
<p><code></code> <code>Long queueSize = (Long) conn.getAttribute(queueName,</code> <code>&quot;QueueSize&quot;</code> <code>);</code> <code>//</code> <code>队列消息数量</code></p>
<p><code></code> <code>Long producerCount = (Long) conn.getAttribute(queueName,</code> <code>&quot;ProducerCount&quot;</code> <code>);</code> <code>//</code> <code>生产者数量</code></p>
<p><code></code> <code>Long consumerCount = (Long) conn.getAttribute(queueName,</code> <code>&quot;ConsumerCount&quot;</code> <code>);</code> <code>//</code> <code>消费者数量</code></p>
<p><code></code> <code>Long enqueueCount = (Long) conn.getAttribute(queueName,</code> <code>&quot;EnqueueCount&quot;</code> <code>);</code> <code>//</code> <code>入队消息总数</code></p>
<p><code></code> <code>Long dequeueCount = (Long) conn.getAttribute(queueName,</code> <code>&quot;DequeueCount&quot;</code> <code>);</code> <code>//</code> <code>出队消息总数</code></p>
<p><code></code> <code>...</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>} catch (Exception e) {</code></p>
<p><code></code> <code>...</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>return</code> <code>datas;</code></p>
<p><code></code> <code>}</code></p>
<p>其中objectName值，可以在jconsole中查到</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(5)：JMX监控/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(5)：JMX监控/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(3)：基于Networks of Brokers的HA方案" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(3)：基于Networks of Brokers的HA方案/">ActiveMQ笔记(3)：基于Networks of Brokers的HA方案</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(3)：基于Networks of Brokers的HA方案/">
            <time datetime="2018-04-19T16:34:21.411Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p><a href="http://blog.csdn.net/lsy0903/article/details/53413355" target="_blank" rel="noopener"> 上一篇介绍了基于ZK的ActiveMQ HA方案
</a> ，虽然理解起来比较容易，但是有二个不足：</p>
<p>1)  占用的节点数过多，1个zk集群至少3个节点，1个activemq集群也至少得3个节点，但其实正常运行时，只有一个master节点在对外响应，换句话说<br>，花6个节点的成本只为了保证1个activemq master节点的高可用，太浪费资源了。</p>
<p>2)  性能下降太明显，比起单节点的activemq，性能下降了近1个数量级。</p>
<p>这一篇将介绍基于networks of<br>brokers的HA方案，不需要借助zk等第3方组件，只需要2个activemq节点就能达到类似效果，进入正题之前，先来简单谈谈Broker这个概念。</p>
<p>Broker一词的原意是『  经纪人、中间人  』，用在ActiveMQ的构架中，即：Broker作为Producer与Consumer的中间人(或代理人)<br>，生产者不用知道消费者在哪里、如何消费这些细节，只要将消息扔给中间人Broker即可，类似的，消费者也不用关心消息是从哪个生产者过来的，它只知道这是从Bro<br>ker那里拿来的，如果画一张图来描述，就是下面这样（引用自本文最后参考文章中的图片）</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160325232947995-77
5199925.gif" alt=""></p>
<p>那么，当生产者将消息发给Broker时，会发生什么？下图描述的就是这个过程：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160325233102823-19
44467648.gif" alt=""></p>
<p>1) 生产者将消息发给Broker</p>
<p>2) Broker将消息落地存储</p>
<p>3) 然后给生产者反馈：事情我已经办妥了！</p>
<p>继续，再来看看消费者又是如何跟Broker打交道的：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160325233400479-19
84865403.gif" alt=""></p>
<p>1) Broker将接收到的消息，从db中取出</p>
<p>2) 然后发送给消费者</p>
<p>3)<br>如果消费者使用的是自动确认模式(即：Session.AUTO_ACKNOWLEDGE），则Consumer会马上告诉Broker：ok，消息我已经收到了。</p>
<p>4) 然后进行自己的业务处理</p>
<p>5) Broker一旦收到确认，将会马上更新消息的状态为已消费(或直接删除，取决于持久化的实现机制）（注：虽然图中步骤5排在步骤4之后，但是步骤4、5几乎是<br>同时发生的）</p>
<p>在一些大型应用中，如果一个Broker出现性能瓶颈抗不住压力，可能会拆分成多个Broker，如下图所示：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326143807511-76
6176344.gif" alt=""></p>
<p>(注：上图中箭头的方法并非数据流向，而应该理解成调用关系，即：Producer调用Broker1，Consumer调用Broker2…)</p>
<p>Producer将消息发送给Broker1，而Consumer从另一个Broker2接收消息，有点类似数据库读写分离的意思，这样系统的性能可以提升一定程度的<br>提升，但是问题来了，Broker1上的消息，如何  “同步”（见下面的注释）  到Broker2呢，这就依赖networkConnector的配置。</p>
<p>注：  _ 同步这个词用在这里可能不太准确，但也找不到一个更精确的词来描述，实际上，二个broker用上述机制组建成小集群后，如果生产者连接到broker1<br>，消费者连接到broker2，当消息发送到broker1后，broker1不会将该消息复制一份到broker2，而是等消费者从broker2上消费该消息时，<br>这条消息才从broker1取到broker2上，相当于此时broker2是消费者，从broker1消费了一条消息，然后broker2上就有这条消息了，最终消<br>费者才能broker2上拿到这条消息。 _</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;beans
 2         xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4         xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 5   http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd&quot;&gt;
 6 
 7   &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
 8     &lt;property name=&quot;locations&quot;&gt;
 9       &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
10     &lt;/property&gt;
11   &lt;/bean&gt;
12 
13   &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;activemq-1&quot;&gt;
14     &lt;networkConnectors&gt;
15       &lt;networkConnector uri=&quot;static:(tcp://127.0.0.1:61626)&quot;/&gt;
16     &lt;/networkConnectors&gt;
17     &lt;persistenceAdapter&gt;
18       &lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;
19     &lt;/persistenceAdapter&gt;
20     &lt;transportConnectors&gt;
21       &lt;transportConnector name=&quot;openwire&quot;
22                           uri=&quot;tcp://0.0.0.0:61616?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
23     &lt;/transportConnectors&gt;
24   &lt;/broker&gt;
25 
26   &lt;import resource=&quot;jetty.xml&quot;/&gt;
27 &lt;/beans&gt;
</code></pre><p>View Code</p>
<p>注意：14-16行及21-22行，该Broker对外暴露616 <strong> 1  </strong> 6端口，同时”连接”到616 <strong> 2  </strong><br>6端口（即另1个broker）,最终的效果相当于，如果有producer把消息发到616 <strong> 1  </strong><br>6（broker1）,则从另一个broker(616 <strong> 2  </strong> 6端口)上也能消费这条消息。</p>
<p>明白这些基本原理后，在61626对应的activemq上，也做类似的配置，只不过”连接方向”正好相反，参考以下配置：</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;beans
 2         xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4         xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 5   http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd&quot;&gt;
 6 
 7     &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
 8         &lt;property name=&quot;locations&quot;&gt;
 9             &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
10         &lt;/property&gt;
11     &lt;/bean&gt;
12 
13     &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;activemq-2&quot;&gt;
14         &lt;networkConnectors&gt;
15             &lt;networkConnector uri=&quot;static:(tcp://127.0.0.1:61616)&quot;/&gt;
16         &lt;/networkConnectors&gt;
17         &lt;persistenceAdapter&gt;
18             &lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;
19         &lt;/persistenceAdapter&gt;
20         &lt;transportConnectors&gt;
21             &lt;transportConnector name=&quot;openwire&quot;
22                                 uri=&quot;tcp://0.0.0.0:61626?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
23         &lt;/transportConnectors&gt;
24     &lt;/broker&gt;
25 
26     &lt;import resource=&quot;jetty.xml&quot;/&gt;
27 &lt;/beans&gt;
</code></pre><p>View Code</p>
<p>(注：如果希望2个activemq上都能访问admin管理界面，jetty.xml中的端口要修改，不要冲突)</p>
<p>这样，activemq-1与activemq-2这二个broker就互为主备，发给你的消息会同步到我，发给我的消息也会同步到你，实现了HA，示意图如下：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326151335604-95
1843855.png" alt=""></p>
<p>Producer与Consumer连接到activemq时，配置文件可以这么写：</p>
<p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<pre><code>1 &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
2     &lt;property name=&quot;connectionFactory&quot;&gt;
3         &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
4             &lt;!--broker服务的地址--&gt;
5             &lt;property name=&quot;brokerURL&quot; value=&quot;failover:(tcp://localhost:61616,tcp://localhost:61626)&quot;/&gt;
6             ...
7         &lt;/bean&gt;
8     &lt;/property&gt;
9 &lt;/bean&gt;
</code></pre><p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<p>这种HA方案的优点是占用的节点数更少(只需要2个节点),而且2个broker都可以响应消息的接收与发送，性能比zookeeper方案要好一些。</p>
<p>最后，再给一个简化配置的技巧，以上述的2节点HA方案中，二个activemq的配置文件都要加networkConnector配置，如果想减轻配置的工作量，也<br>可以只在其中一个activemq设置，参考以下片段：</p>
<pre><code>&lt;networkConnectors&gt;
  &lt;networkConnector uri=&quot;static:(tcp://127.0.0.1:61626)&quot; duplex=&quot;true&quot;/&gt;
&lt;/networkConnectors&gt;
</code></pre><p>即：在61616这个activemq的配置文件中，添加networkConnector时，增加  duplex=”true”  ，也就是双工通讯的意思，这样<br>61616与61626这二个activemq上的broker就建立了双向通讯连接，另一个activemq上就无需额外配置了（注：如果在61626上配置了，反<br>而会报错）</p>
<p><strong> 参考文章：  </strong></p>
<p><a href="http://www.jakubkorab.net/2011/11/understanding-activemq-
broker-networks.html" target="_blank" rel="noopener"> http://www.jakubkorab.net/2011/11/understanding-activemq-broker-<br>networks.html  </a></p>
<p><a href="http://activemq.apache.org/networks-of-brokers.html" target="_blank" rel="noopener"> http://activemq.apache.org/networks-of-brokers.html
</a></p>
<p><strong> 最后  </strong> 贴二段程序运行的输出日志，以说明同步机制的正确性，打消回复中“大鹏520”的顾虑： </p>
<p>背景： <em>.</em>.<em>.15 与 </em>.<em>.</em>.16 作为HA(双主)的activemq集群，发送程序只连接到15发消息，然后退出。<br>接收程序只从16上收消息，如果收到了，表明15上的消息同步到16。</p>
<p>下面是 <strong> 发送程序 </strong> 的输出片段：（注意输出日志中关于IP的部分，这是只连接到<em>.</em>.<em>.[b]15[/b]上发送的）<br>(注：部分敏感信息，比如真实IP前缀，公司package名，用</em>代替了)</p>
<p>_ 14:53:09,996 &lt;<em>.DemoSender&gt; INFO [main]: 准备发送消息… _<br>_ 14:53:10,270 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[main]: Sending: WireFormatInfo { version=11,<br>properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024,<br>StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true,<br>MaxFrameSize=9223372036854775807, Host=</em>.<em>.</em>.15, MaxInactivityDuration=30000,<br>MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:53:10,306 &lt;org.apache.activemq.transport.InactivityMonitor&gt; DEBUG<br>[ActiveMQ Transport: tcp:///<em>.</em>.<em>. <strong> 15  </strong> :61616@50616]: Using min of<br>local: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true,<br>SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true,<br>CacheEnabled=true, TightEncodingEnabled=true,<br>MaxFrameSize=9223372036854775807, Host=</em>.<em>.</em>.15, MaxInactivityDuration=30000,<br>MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]} and remote:<br>WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true,<br>SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true,<br>CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600,<br>MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000},<br>magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:53:10,306 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///<em>.</em>.<em>. <strong> 15  </strong> :61616@50616]: Received<br>WireFormat: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true,<br>SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true,<br>CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600,<br>MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000},<br>magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:53:10,306 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///</em>.<em>.</em>. <strong> 15  </strong> :61616@50616]:<br>tcp:///<em>.</em>.<em>.15:61616@50616 before negotiation: OpenWireFormat{version=11,<br>cacheEnabled=false, stackTraceEnabled=false, tightEncodingEnabled=false,<br>sizePrefixDisabled=false, maxFrameSize=9223372036854775807} _<br>_ 14:53:10,307 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///</em>.<em>.</em>. <strong> 15  </strong> :61616@50616]:<br>tcp:///<em>.</em>.<em>.15:61616@50616 after negotiation: OpenWireFormat{version=11,<br>cacheEnabled=true, stackTraceEnabled=true, tightEncodingEnabled=true,<br>sizePrefixDisabled=false, maxFrameSize=104857600} _<br>_ 14:53:10,416 &lt;org.springframework.jms.core.JmsTemplate&gt; DEBUG [main]:<br>Executing callback on JMS Session: PooledSession { ActiveMQSession {id=ID<br>:yangjunmings-MacBook-Pro.local-50615-1461480790088-1:1:1,started=false}<br>java.lang.Object@5a56cdac } _<br>_ 14:53:10,471 &lt;org.springframework.jms.core.JmsTemplate&gt; DEBUG [main]:<br>Sending created message: ActiveMQTextMessage {commandId = 0, responseRequired<br>= false, messageId = null, originalDestination = null, originalTransactionId =<br>null, producerId = null, destination = null, transactionId = null, expiration<br>= 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0,<br>correlationId = null, replyTo = null, persistent = false, type = null,<br>priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null,<br>compressed = false, userID = null, content = null, marshalledProperties =<br>null, dataStructure = null, redeliveryCounter = 0, size = 0, properties =<br>null, readOnlyProperties = false, readOnlyBody = false, droppable = false,<br>jmsXGroupFirstForConsumer = false, text =  message test:0  } _<br>_ 14:53:10,516 &lt;</em>.DemoSender&gt; INFO [main]:  消息0发送完成!  _<br>_ 14:53:11,520 &lt;org.springframework.jms.core.JmsTemplate&gt; DEBUG [main]:<br>Executing callback on JMS Session: PooledSession { ActiveMQSession {id=ID<br>:yangjunmings-MacBook-Pro.local-50615-1461480790088-1:1:1,started=false}<br>java.lang.Object@5a56cdac } _<br>_ 14:53:11,521 &lt;org.springframework.jms.core.JmsTemplate&gt; DEBUG [main]:<br>Sending created message: ActiveMQTextMessage {commandId = 0, responseRequired<br>= false, messageId = null, originalDestination = null, originalTransactionId =<br>null, producerId = null, destination = null, transactionId = null, expiration<br>= 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0,<br>correlationId = null, replyTo = null, persistent = false, type = null,<br>priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null,<br>compressed = false, userID = null, content = null, marshalledProperties =<br>null, dataStructure = null, redeliveryCounter = 0, size = 0, properties =<br>null, readOnlyProperties = false, readOnlyBody = false, droppable = false,<br>jmsXGroupFirstForConsumer = false, text = message test:1} _</p>
<p>运行完以后，关掉发送程序，然后启动 <strong> 接收程序 </strong> ：</p>
<p>_ 14:54:40,965 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[main]: Sending: WireFormatInfo { version=11,<br>properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024,<br>StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true,<br>MaxFrameSize=9223372036854775807, Host=<em>.</em>.<em>.  <strong> 16 </strong> ,<br>MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000},<br>magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:54:41,002 &lt;org.apache.activemq.transport.InactivityMonitor&gt; DEBUG<br>[ActiveMQ Transport: tcp:///</em>.<em>.</em>.  <strong> 16 </strong> :61616@50642]: Using min of<br>local: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true,<br>SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true,<br>CacheEnabled=true, TightEncodingEnabled=true,<br>MaxFrameSize=9223372036854775807, Host=<em>.</em>.<em>.  <strong> 16 </strong> ,<br>MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000},<br>magic=[A,c,t,i,v,e,M,Q]} and remote: WireFormatInfo { version=11,<br>properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024,<br>StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true,<br>MaxFrameSize=104857600, MaxInactivityDuration=30000,<br>MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:54:41,003 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///</em>.<em>.</em>.  <strong> 16 </strong> :61616@50642]: Received<br>WireFormat: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true,<br>SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true,<br>CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600,<br>MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000},<br>magic=[A,c,t,i,v,e,M,Q]} _<br>_ 14:54:41,003 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///<em>.</em>.<em>.  <strong> 16 </strong> :61616@50642]: tcp:///</em>.<em>.</em>.  <strong><br>16 </strong> :61616@50642 before negotiation: OpenWireFormat{version=11,<br>cacheEnabled=false, stackTraceEnabled=false, tightEncodingEnabled=false,<br>sizePrefixDisabled=false, maxFrameSize=9223372036854775807} _<br>_ 14:54:41,004 &lt;org.apache.activemq.transport.WireFormatNegotiator&gt; DEBUG<br>[ActiveMQ Transport: tcp:///<em>.</em>.<em>.  <strong> 16 </strong> :61616@50642]:<br>tcp:///</em>.<em>.</em>.16:61616@50642 after negotiation: OpenWireFormat{version=11,<br>cacheEnabled=true, stackTraceEnabled=true, tightEncodingEnabled=true,<br>sizePrefixDisabled=false, maxFrameSize=104857600} _<br>_ 14:54:41,163 &lt;org.apache.activemq.thread.TaskRunnerFactory&gt; DEBUG [main]:<br>Initialized TaskRunnerFactory[ActiveMQ Session Task] using ExecutorService:<br>java.util.concurrent.ThreadPoolExecutor@485966cc[Running, pool size = 0,<br>active threads = 0, queued tasks = 0, completed tasks = 0] _<br>_ 14:54:41,181 &lt;*.DemoListener&gt; INFO [ActiveMQ Session Task-1]:  message<br>test:0  _</p>
<p>注意接收程序中IP的部分，这是从<em>.</em>.<em>.[b]16[/b]上接收到的，说明消息已经从 </em>.<em>.</em>.15同步到<em>.</em>.*.16上了，否则不可能收到消息。</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(3)：基于Networks of Brokers的HA方案/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(3)：基于Networks of Brokers的HA方案/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(4)：搭建Broker集群(cluster)" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(4)：搭建Broker集群(cluster)/">ActiveMQ笔记(4)：搭建Broker集群(cluster)</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(4)：搭建Broker集群(cluster)/">
            <time datetime="2018-04-19T16:34:21.411Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>, <a class="tag-link" href="/tags/集群/">集群</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <h1 id="ActiveMQ笔记-4-：搭建Broker集群-cluster"><a href="#ActiveMQ笔记-4-：搭建Broker集群-cluster" class="headerlink" title="[ ActiveMQ笔记(4)：搭建Broker集群(cluster)"></a>[ ActiveMQ笔记(4)：搭建Broker集群(cluster)</h1><p>](<a href="http://blog.csdn.net/lsy0903/article/details/54862508" target="_blank" rel="noopener">http://blog.csdn.net/lsy0903/article/details/54862508</a>)</p>
<p><a href="http://blog.csdn.net/lsy0903/article/details/53413417" target="_blank" rel="noopener"> 上一篇 </a> 介绍了基于Networks<br>of Borkers的2节点HA方案，这一篇继续来折腾Networks of Brokers，当应用规模日渐增长时，2节点的broker可能仍然抗不住访问压<br>力，这时候就需要多加一些broker，弄一个更大规模的Broker集群，但是怎么合理设置broker之间的网络桥接，却是有讲究的，先来看一种不太好的设计：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326233603792-51
0527302.gif" alt=""></p>
<p>这个架构看上去没瑕疵，没毛病，3个broker之间两两互通，整体可用性极高，但是从消息的路由角度来看，却不是一个好的设计，当producer向broker1<br>发送一条消息时，Consumer得到消息的路径可能有如下2条：</p>
<p>a） producer -&gt; broker1 -&gt; broker2</p>
<p>b） producer -&gt; broker1 -&gt; broker3 -&gt; broker2</p>
<p>当broker更多时，情况会更复杂，比如下面这张图：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326234011073-18
15268387.gif" alt=""></p>
<p>消息的路由途径将会更多：</p>
<p>a) producer -&gt; broker1 -&gt; broker4</p>
<p>b) producer -&gt; broker1 -&gt; broker2 -&gt; broker4</p>
<p>c) producer -&gt; broker1 -&gt; broker2 -&gt; broker3 -&gt; broker4</p>
<p>d) producer -&gt; broker1 -&gt; broker3 -&gt; broker4</p>
<p>不难想像，每多经过一个节点，消息处理的延时将会增加一些，如果Broker越多，情况越复杂，最终系统对外表现为消息处理有时很快，有时很慢，整体性能很不稳定，所<br>以  实际生产中，不要采用所有Broker之间两两互连的方案  。</p>
<p>合理的方案如下：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326234618104-16
30004598.gif" alt=""></p>
<p>这张图的灵感，应该来自组建局域网中的星形网络，在中心放置一个Borker充当Hub，与其它所有Broker互连，这样不管Consumer连接到外围的哪个Br<br>oker，消息的路由途径都比较稳定(最多经过3个Broker)，这种架构性能虽然稳定了，但是中心的Hub就变成单点隐患，如果中间的DockerHub挂了，整<br>个系统也就废了。</p>
<p>改进后的架构如下：</p>
<p><img src="http://images2015.cnblogs.com/blog/27612/201603/27612-20160326235113183-20
49846046.png" alt=""></p>
<p>本质上仍然是一个星形网络，只不过将hub弄成二个互备，然后每个hub都与其它外围的broker相连，消费者连接到broker1/broker2/broker<br>3，生产者(Producer)连接到hub1/hub2，消息的最长路径不超过3个broker<br>(注：生产者也可以连接到broker1/2/3，与消费者一样，但是消息经过的最长路径会变成4)</p>
<p>如果以后要扩张，比如增加了Broker4,Broker5…，直接修改hub1/2上的配置，增加与新的broker的连接即可，不影响消息的路由途径长度。</p>
<p>最后，在本机演练一把，给出一些配置示例：</p>
<p>1、  端口规划</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p><code>activemq1: 61616 (broker1)</code></p>
<p><code>activemq2: 61626 (broker2)</code></p>
<p><code>activemq3: 61636 (broker3)</code></p>
<p><code>activemq4: 61646 (broker-hub1)</code></p>
<p><code>activemq5: 61656 (broker-hub2)</code></p>
<p>共5个activemq实例，端口61616、61626、61636为broker1、broker2、broker3，61645、61656为broker-<br>hub1、broker-hub2</p>
<p>2、  activemq.xml配置</p>
<p>以boker1为例，配置文件内容如下：</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;beans
 2         xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4         xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 5   http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd&quot;&gt;
 6 
 7   &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
 8     &lt;property name=&quot;locations&quot;&gt;
 9       &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
10     &lt;/property&gt;
11   &lt;/bean&gt;
12 
13   &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;broker1&quot;&gt;  
14     &lt;persistenceAdapter&gt;
15       &lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;
16     &lt;/persistenceAdapter&gt;
17     &lt;transportConnectors&gt;
18       &lt;transportConnector name=&quot;openwire&quot;
19                           uri=&quot;tcp://0.0.0.0:61616?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
20     &lt;/transportConnectors&gt;
21   &lt;/broker&gt;
22 
23   &lt;import resource=&quot;jetty.xml&quot;/&gt;
24 &lt;/beans&gt;
</code></pre><p>View Code</p>
<p>broker2及broker3，大家参考该配置修改端口号及brokerName即可。</p>
<p>broker-hub1的配置：</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;beans
 2         xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4         xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 5   http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd&quot;&gt;
 6 
 7   &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
 8     &lt;property name=&quot;locations&quot;&gt;
 9       &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
10     &lt;/property&gt;
11   &lt;/bean&gt;
12 
13   &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;broker-hub1&quot;&gt;
14     &lt;networkConnectors&gt;
15       &lt;networkConnector uri=&quot;static:(tcp://127.0.0.1:61656,tcp://127.0.0.1:61616,tcp://127.0.0.1:61626,tcp://127.0.0.1:61636)&quot; duplex=&quot;true&quot;/&gt;      
16     &lt;/networkConnectors&gt;
17     &lt;persistenceAdapter&gt;
18       &lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;
19     &lt;/persistenceAdapter&gt;
20     &lt;transportConnectors&gt;
21       &lt;transportConnector name=&quot;openwire&quot;
22                           uri=&quot;tcp://0.0.0.0:61646?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
23     &lt;/transportConnectors&gt;
24   &lt;/broker&gt;
25 
26   &lt;import resource=&quot;jetty.xml&quot;/&gt;
27 &lt;/beans&gt;
</code></pre><p>broker-hub2的配置：</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;beans
 2         xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4         xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 5   http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd&quot;&gt;
 6 
 7   &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
 8     &lt;property name=&quot;locations&quot;&gt;
 9       &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
10     &lt;/property&gt;
11   &lt;/bean&gt;
12 
13   &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;broker-hub2&quot;&gt;
14     &lt;networkConnectors&gt;   
15       &lt;networkConnector uri=&quot;static:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61626,tcp://127.0.0.1:61636)&quot; duplex=&quot;true&quot;/&gt;
16     &lt;/networkConnectors&gt;
17     &lt;persistenceAdapter&gt;
18       &lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;
19     &lt;/persistenceAdapter&gt;
20     &lt;transportConnectors&gt;
21       &lt;transportConnector name=&quot;openwire&quot;
22                           uri=&quot;tcp://0.0.0.0:61656?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
23     &lt;/transportConnectors&gt;
24   &lt;/broker&gt;
25 
26   &lt;import resource=&quot;jetty.xml&quot;/&gt;
27 &lt;/beans&gt;
</code></pre><p>3、  java代码中spring配置文件</p>
<p>a)  生产者</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
 2     &lt;property name=&quot;connectionFactory&quot;&gt;
 3         &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
 4             &lt;!--broker服务的地址--&gt;
 5             &lt;property name=&quot;brokerURL&quot; value=&quot;failover:(tcp://localhost:61646,tcp://localhost:61656)&quot;/&gt;
 6             &lt;!--默认值为1000,如果不需要这么大,可以调小--&gt;
 7             &lt;property name=&quot;maxThreadPoolSize&quot; value=&quot;100&quot;/&gt;
 8             &lt;!--&lt;property name=&quot;userName&quot; value=&quot;system&quot;/&gt;--&gt;
 9             &lt;!--&lt;property name=&quot;password&quot; value=&quot;manager&quot;/&gt;--&gt;
10         &lt;/bean&gt;
11     &lt;/property&gt;
12 &lt;/bean&gt;
</code></pre><p>b)  消费者</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
 2     &lt;property name=&quot;connectionFactory&quot;&gt;
 3         &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
 4             &lt;!--broker服务的地址--&gt;
 5             &lt;property name=&quot;brokerURL&quot; value=&quot;failover:(tcp://localhost:61616,tcp://localhost:61626,tcp://localhost:61636)&quot;/&gt;
 6             &lt;!--默认值为1000,如果不需要这么大,可以调小--&gt;
 7             &lt;property name=&quot;maxThreadPoolSize&quot; value=&quot;100&quot;/&gt;
 8             &lt;!--&lt;property name=&quot;userName&quot; value=&quot;system&quot;/&gt;--&gt;
 9             &lt;!--&lt;property name=&quot;password&quot; value=&quot;manager&quot;/&gt;--&gt;
10         &lt;/bean&gt;
11     &lt;/property&gt;
12 &lt;/bean&gt;
</code></pre>
        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(4)：搭建Broker集群(cluster)/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(4)：搭建Broker集群(cluster)/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(2)：基于ZooKeeper的HA方案" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(2)：基于ZooKeeper的HA方案/">ActiveMQ笔记(2)：基于ZooKeeper的HA方案</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(2)：基于ZooKeeper的HA方案/">
            <time datetime="2018-04-19T16:34:21.410Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>, <a class="tag-link" href="/tags/zookeeper/">zookeeper</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>activemq官网给出了3种master/slave的HA方案，详见： <a href="http://activemq.apache.org/masterslave.html" target="_blank" rel="noopener"><br>http://activemq.apache.org/masterslave.html
</a> ，基于共享文件目录，db，zookeeper。</p>
<p>下面演示了如何在本机搭建基于zookeeper的activemq集群：</p>
<p>一、  在目录activemq1下安装activemq(可参考 <a href="http://blog.csdn.net/lsy0903/article/details/53413271" target="_blank" rel="noopener"> 上篇内容
</a><br>)，然后修改conf/activemq.xml</p>
<p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<pre><code> 1     &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;localhost&quot; dataDirectory=&quot;${activemq.data}&quot;&gt;
 2         ...
 3         &lt;persistenceAdapter&gt;
 4             &lt;!--&lt;kahaDB directory=&quot;${activemq.data}/kahadb&quot;/&gt;--&gt;
 5             &lt;replicatedLevelDB
 6                     directory=&quot;activemq-data&quot;
 7                     replicas=&quot;3&quot;
 8                     bind=&quot;tcp://0.0.0.0:0&quot;
 9                     zkAddress=&quot;127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183&quot;
10                     zkSessionTimeout=&quot;2s&quot;
11                     zkPath=&quot;/activemq/leveldb-stores&quot;
12             /&gt;
13         &lt;/persistenceAdapter&gt;
14         ...
15     &lt;/broker&gt;
</code></pre><p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<p>注：为保证zk的HA，本机至少要有3个zk的节点，可参考 <a href="http://www.cnblogs.com/yjmyzz/p/4587663.html" target="_blank" rel="noopener"> 我以前的文章
</a> 搭建.</p>
<p>二、  将activemq1复制二分，变成activemq2、activemq3，由于是在本机测试，为防止端口冲突，这二个目录下的activemq.xml，<br>得修改端口</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p><code>&lt;transportConnectors&gt;</code></p>
<p><code></code> <code>&lt;transportConnector name=</code> <code>&quot;openwire&quot;</code></p>
<p><code></code> <code>uri=</code> <code>&quot;tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFram
eSize=104857600&quot;</code> <code>/&gt;</code></p>
<p><code></code> <code>&lt;transportConnector name=</code> <code>&quot;amqp&quot;</code></p>
<p><code></code> <code>uri=</code> <code>&quot;amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFram
eSize=104857600&quot;</code> <code>/&gt;</code></p>
<p><code></code> <code>&lt;transportConnector name=</code> <code>&quot;stomp&quot;</code></p>
<p><code></code> <code>uri=</code> <code>&quot;stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFr
ameSize=104857600&quot;</code> <code>/&gt;</code></p>
<p><code></code> <code>&lt;transportConnector name=</code> <code>&quot;mqtt&quot;</code></p>
<p><code></code> <code>uri=</code> <code>&quot;mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFram
eSize=104857600&quot;</code> <code>/&gt;</code></p>
<p><code></code> <code>&lt;transportConnector name=</code> <code>&quot;ws&quot;</code></p>
<p><code></code> <code>uri=</code> <code>&quot;ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600&quot;</code> <code>/&gt;</code></p>
<p><code>&lt;/transportConnectors&gt;</code></p>
<p>上面这几个端口，大家看情况调整，只要保证3个activemq不冲突即可</p>
<p>三、  启动zk1,zk2,zk3，以及activemq1,activemq2,activemq3即可。</p>
<p>注：为方便观察输出，建议启动activemq时，用./activemq.sh console启动</p>
<p>四、  测试Failover</p>
<p>正常启动后，然后手动停掉master，然后观察剩下的2个节点终端输出，正常情况下，应该过一会儿，有一个会自动提升为master.</p>
<p>最后提醒一下：采用上述HA方案后，虽然系统可用性提高了，但是在本机上测试发现，跟 <a href="http://www.cnblogs.com/yjmyzz/p/activemq-sample.html" target="_blank" rel="noopener"> 上篇
</a> 同样的测试代码和用例，单节点运行时，1秒可以<br>发8k+条消息，采用zookeeper的HA方案后，每秒只能写入500条消息左右，对于性能要求较高的场景，建议采用其它方案，比如下一篇要介绍的 <a href="http://www.cnblogs.com/yjmyzz/p/activemq-ha-
using-networks-of-brokers.html" target="_blank" rel="noopener"><br>基于Networks of brokers的HA方案 </a> 。</p>
<p><strong> 参考文章： </strong></p>
<p><a href="http://activemq.apache.org/replicated-leveldb-store.html" target="_blank" rel="noopener"> </a> <a href="http://activemq.apache.org/replicated-leveldb-store.html" target="_blank" rel="noopener"><br>http://activemq.apache.org/replicated-leveldb-store.html
</a></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(2)：基于ZooKeeper的HA方案/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(2)：基于ZooKeeper的HA方案/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-ActiveMQ笔记(1)：编译、安装、示例代码" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/ActiveMQ笔记(1)：编译、安装、示例代码/">ActiveMQ笔记(1)：编译、安装、示例代码</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/ActiveMQ笔记(1)：编译、安装、示例代码/">
            <time datetime="2018-04-19T16:34:21.409Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ActiveMQ/">ActiveMQ</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/activemq/">activemq</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>一、编译</p>
<p>虽然 <a href="http://activemq.apache.org/" target="_blank" rel="noopener"> ActiveMQ </a><br>提供了发布版本，但是建议同学们自己下载源代码编译，以后万一有坑，还可以尝试自己改改源码。</p>
<p>1.1  <a href="https://github.com/apache/activemq/releases" target="_blank" rel="noopener"> https://github.com/apache/activemq/releases
</a><br>到这里下载最新的release版源码（当前最新版本为5.13.2），并解压到某个目录(以下用$ACTIVEMQ_HOME代替解压根目录)</p>
<p>1.2  编译</p>
<p>1</p>
<p>2</p>
<p><code>cd</code> <code>$ACTIVEMQ_HOME</code></p>
<p><code>mvn clean</code> <code>install</code> <code>-Dmaven.</code> <code>test</code> <code>.skip=</code> <code>true</code></p>
<p>编译成功后，在$ACTIVEMQ_HOME  /assembly/target下会生成可xxx.bin.tar.gz的可执行文件压缩包</p>
<p>二、启动</p>
<p>将编译后得到的xxx.bin.tar.gz解压，然后执行</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p><code>tar</code> <code>-zxvf apache-activemq-5.13.2-bin.</code> <code>tar</code> <code>.gz</code></p>
<p><code>cd</code> <code>apache-activemq-5.13.2</code> <code>/bin</code></p>
<p><code>.</code> <code>/activemq</code> <code>start</code></p>
<p>后面的可选参数还有 status、restart、stop、list等，不清楚的地方，直接 –help 查看。</p>
<p>注：  生产环境中，可能会对activemq的jvm内存设置上限，可以直接修改bin/activemq启动脚本，vi bin/activemq<br>找到下面的位置：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p><code># Note: This function uses globally defined variables</code></p>
<p><code># - $ACTIVEMQ_PIDFILE : the name of the pid file&lt;/code&gt;&lt;/div&gt;&lt;div
class=&quot;line number3 index2 alt2&quot;&gt;&lt;code class=&quot;bash comments&quot;&gt;# -
$ACTIVEMQ_OPTS : Additional options</code></p>
<p><code>ACTIVEMQ_OPTS=</code> <code>&quot;-server -Xms512M -Xmx512M -XX:PermSize=64M
-XX:MaxPermSize=128M &quot;</code></p>
<p><code># - $ACTIVEMQ_SUNJMX_START : options for JMX settings&lt;/code&gt;&lt;/div&gt;&lt;div
class=&quot;line number6 index5 alt1&quot;&gt;&lt;code class=&quot;bash comments&quot;&gt;# -
$ACTIVEMQ_SSL_OPTS : options for SSL encryption</code></p>
<p>设置ACTIVEMQ_OPTS即可，然后重启activemq，建议启动成功后，用jinfo {activemq的pid} 来验证查看一下</p>
<p>三、管理界面</p>
<p>启动成功后，可以浏览 <a href="http://localhost:8161/admin/" target="_blank" rel="noopener"> http://localhost:8161/admin/ </a></p>
<p>默认用户名、密码：admin/admin</p>
<p>管理界面是用jetty做容器的，如果想修改管理界面的端口，可以编辑../conf/jetty.xml，找到下面这一段：</p>
<pre><code>&lt;bean id=&quot;jettyPort&quot; class=&quot;org.apache.activemq.web.WebConsolePort&quot; init-method=&quot;start&quot;&gt;
    &lt;!-- the default port number for the web console --&gt;
    &lt;property name=&quot;host&quot; value=&quot;0.0.0.0&quot;/&gt;
    &lt;property name=&quot;port&quot; value=&quot;8161&quot;/&gt;
&lt;/bean&gt;
</code></pre><p>用户名/密码是在 ../conf/jetty-realm.properties 里，比如要增加一个管理员jimmy/123456，可参考下面修改：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p><code>admin: admin, admin</code></p>
<p><code>jimmy: 123456, admin</code></p>
<p><code>user: user, user</code></p>
<p>注：  管理界面有一个  小坑  ，ActiveMQ 5.13.  2  与jdk1.8兼容性有点问题，如果使用jdk1.8，管理界面进入Queues标签页<br>时，偶尔会报错，但是并不影响消息正常收发，只是无法从界面上查看队列情况，如果出现该问题，可将jdk版本降至1.7，同时最好清空data目录下的所有数据，再重<br>启activemq即可。</p>
<p>2016-06-18 注：  最新版的5.13.  3  已经修复了这个bug，建议大家使用最新版本。</p>
<p>四、示例代码</p>
<p>通常消息队列都支持二种模式：基于主题(topic)的发布(Publish)/订阅(Subscribe)模式、点对点(p2p)模式，下面的示例代码为p2p场景<br>。</p>
<p>先给出gradle项目的依赖项</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p><code>dependencies {</code></p>
<p><code></code> <code>compile</code> <code>&quot;org.springframework:spring-core:4.2.5.RELEASE&quot;</code></p>
<p><code></code> <code>compile</code> <code>&quot;org.springframework:spring-beans:4.2.5.RELEASE&quot;</code></p>
<p><code></code> <code>compile</code> <code>&quot;org.springframework:spring-context:4.2.5.RELEASE&quot;</code></p>
<p><code></code> <code>compile</code> <code>&quot;org.springframework:spring-jms:4.2.3.RELEASE&quot;</code></p>
<p><code></code> <code>compile</code> <code>&#39;org.apache.activemq:activemq-all:5.13.2&#39;</code></p>
<p><code></code> <code>compile</code> <code>&#39;org.apache.commons:commons-pool2:2.4.2&#39;</code></p>
<p><code></code> <code>testCompile group:</code> <code>&#39;junit&#39;</code> <code>, name:</code> <code>&#39;junit&#39;</code> <code>, version:</code><br><code>&#39;4.12&#39;</code></p>
<p><code>}</code></p>
<p>4.1  spring配置文件</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
 2 &lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 3        xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 4        xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;
 5 
 6     &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
 7         &lt;property name=&quot;connectionFactory&quot;&gt;
 8             &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
 9                 &lt;!--broker服务的地址--&gt;
10                 &lt;property name=&quot;brokerURL&quot; value=&quot;tcp://localhost:61616&quot;/&gt;
11                 &lt;!--默认值为1000,如果不需要这么大,可以调小--&gt;
12                 &lt;property name=&quot;maxThreadPoolSize&quot; value=&quot;100&quot;/&gt;
13             &lt;/bean&gt;
14         &lt;/property&gt;
15     &lt;/bean&gt;
16 
17     &lt;bean id=&quot;dest&quot; class=&quot;org.apache.activemq.command.ActiveMQQueue&quot;&gt;
18         &lt;!--队列名称--&gt;
19         &lt;property name=&quot;physicalName&quot; value=&quot;myQueue&quot;/&gt;
20     &lt;/bean&gt;
21 
22     &lt;bean id=&quot;myJmsTemplate&quot; class=&quot;org.springframework.jms.core.JmsTemplate&quot;&gt;
23         &lt;property name=&quot;connectionFactory&quot; ref=&quot;jmsFactory&quot;/&gt;
24         &lt;!--默认的队列--&gt;
25         &lt;property name=&quot;defaultDestination&quot; ref=&quot;dest&quot;/&gt;
26         &lt;!--接收超时时间10秒--&gt;
27         &lt;property name=&quot;receiveTimeout&quot; value=&quot;10000&quot;/&gt;
28     &lt;/bean&gt;
29 
30 &lt;/beans&gt;
</code></pre><p>View Code</p>
<p>注：brokerURL的地址是在conf/activemq.xml里定义里，见下面的片段</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code>1 &lt;transportConnectors&gt;
2  &lt;!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB --&gt;
3  &lt;transportConnector name=&quot;openwire&quot; uri=&quot;tcp://0.0.0.0:61616?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
4  &lt;transportConnector name=&quot;amqp&quot; uri=&quot;amqp://0.0.0.0:5672?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
5  &lt;transportConnector name=&quot;stomp&quot; uri=&quot;stomp://0.0.0.0:61613?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
6  &lt;transportConnector name=&quot;mqtt&quot; uri=&quot;mqtt://0.0.0.0:1883?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
7  &lt;transportConnector name=&quot;ws&quot; uri=&quot;ws://0.0.0.0:61614?maximumConnections=1000&amp;amp;wireFormat.maxFrameSize=104857600&quot;/&gt;
8 &lt;/transportConnectors&gt;
</code></pre><p>View Code</p>
<p>另外，连接ActiveMQ默认情况下，没有任何安全机制，也就是说任何人只要知道brokerURL都能连接，这显然不安全，可以在activemq.xml里，找<br>到<broker>节点，紧贴它的地方添加下面这段：</broker></p>
<p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<pre><code>    &lt;broker xmlns=&quot;http://activemq.apache.org/schema/core&quot; brokerName=&quot;localhost&quot; dataDirectory=&quot;${activemq.data}&quot;&gt;

        &lt;plugins&gt;   
            &lt;simpleAuthenticationPlugin&gt;   
                &lt;users&gt;   
                    &lt;authenticationUser username=&quot;${activemq.username}&quot;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt; password&lt;/span&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;=&quot;${activemq.password}&quot; groups=&quot;users,admins&quot;/&gt;   
                &lt;/users&gt;   
            &lt;/simpleAuthenticationPlugin&gt;   
        &lt;/plugins&gt;
...
   &lt;/broker&gt;
</code></pre><p><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></p>
<p>那么问题来了，这个  activemq.username&#x53CA;” role=”presentation”&gt; a  c  t  i  v  e  m<br>q  .  u  s  e  r  n  a  m  e  及  activemq.username及<br>{activemq.password}的值是在哪里定义的呢？仍然在activemq.xml里找答案，在最开始的地方有一段：</p>
<pre><code>1 &lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
2     &lt;property name=&quot;locations&quot;&gt;
3         &lt;value&gt;file:${activemq.conf}/credentials.properties&lt;/value&gt;
4     &lt;/property&gt;
5 &lt;/bean&gt;
</code></pre><p>换句话说，conf/credentials.properties这里保存的就是连接activemq的用户名和密码，启用连接的安全机制后，spring的配置文<br>件要做如下调整：</p>
<p><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><br><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""></p>
<pre><code> 1 &lt;bean id=&quot;jmsFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot; destroy-method=&quot;stop&quot;&gt;
 2     &lt;property name=&quot;connectionFactory&quot;&gt;
 3         &lt;bean class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
 4             &lt;!--broker服务的地址--&gt;
 5             &lt;property name=&quot;brokerURL&quot; value=&quot;tcp://localhost:61616&quot;/&gt;
 6             &lt;!--默认值为1000,如果不需要这么大,可以调小--&gt;
 7             &lt;property name=&quot;maxThreadPoolSize&quot; value=&quot;100&quot;/&gt;
 8             &lt;property name=&quot;userName&quot; value=&quot;system&quot;/&gt;
 9             &lt;property name=&quot;password&quot; value=&quot;manager&quot;/&gt;
10         &lt;/bean&gt;
11     &lt;/property&gt;
12 &lt;/bean&gt;
</code></pre><p>View Code</p>
<p>4.2  生产者代码</p>
<p>发送消息的代码有二种写法：</p>
<p>a）  利用spring-jms的JmsTemplate</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p><code>package</code> <code>com.cnblogs.yjmyzz.activemq;</code></p>
<p><code>import</code> <code>org.springframework.context.ApplicationContext;</code></p>
<p><code>import</code> <code>org.springframework.context.support.ClassPathXmlApplicationContext;</code></p>
<p><code>import</code> <code>org.springframework.jms.core.JmsTemplate;</code></p>
<p><code>/**</code></p>
<p><code></code> <code>* ActiveMQ消息发送示例（利用JMSTemplate）</code></p>
<p><code></code> <code>* Author:菩提树下的杨过 http://yjmyzz.cnblogs.com</code></p>
<p><code></code> <code>*/</code></p>
<p><code>public</code> <code>class</code> <code>JmsTemplateProducer {</code></p>
<p><code></code> <code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code></p>
<p><code></code> <code>ApplicationContext context =</code> <code>new</code> <code>ClassPathXmlApplicationContext(</code> <code>&quot;spring-context.xml&quot;</code> <code>);</code></p>
<p><code></code> <code>JmsTemplate jmsTemplate = context.getBean(JmsTemplate.</code> <code>class</code> <code>);</code></p>
<p><code></code> <code>System.out.println(</code> <code>&quot;准备发送消息...&quot;</code> <code>);</code></p>
<p><code></code> <code>int</code> <code>max =</code> <code>100000</code> <code>;</code></p>
<p><code></code> <code>Long start = System.currentTimeMillis();</code></p>
<p><code></code> <code>for</code> <code>(</code> <code>int</code> <code>i =</code> <code>0</code> <code>; i &lt; max; i++) {</code></p>
<p><code></code> <code>jmsTemplate.convertAndSend(</code> <code>&quot;message test:&quot;</code> <code>\+ i);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>Long end = System.currentTimeMillis();</code></p>
<p><code></code> <code>Long elapse = end - start;</code></p>
<p><code></code> <code>int</code> <code>perform = Double.valueOf(max / (elapse / 1000d)).intValue();</code></p>
<p><code></code> <code>System.out.print(</code> <code>&quot;发送 &quot;</code> <code>\+ max +</code> <code>&quot; 条消息，耗时：&quot;</code> <code>\+ elapse +</code> <code>&quot;毫秒，平均&quot;</code> <code>\+ perform +</code> <code>&quot;条/秒&quot;</code> <code>);</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>b)  利用activeMQ的Producer</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>38</p>
<p>39</p>
<p>40</p>
<p>41</p>
<p>42</p>
<p>43</p>
<p>44</p>
<p>45</p>
<p>46</p>
<p><code>package</code> <code>com.cnblogs.yjmyzz.activemq;</code></p>
<p><code>import</code> <code>org.apache.activemq.command.ActiveMQQueue;</code></p>
<p><code>import</code> <code>org.apache.activemq.pool.PooledConnectionFactory;</code></p>
<p><code>import</code> <code>org.springframework.context.ApplicationContext;</code></p>
<p><code>import</code> <code>org.springframework.context.support.ClassPathXmlApplicationContext;</code></p>
<p><code>import</code> <code>javax.jms.*;</code></p>
<p><code>import</code> <code>java.io.IOException;</code></p>
<p><code>/**</code></p>
<p><code></code> <code>* ActiveMQ消息发送示例（利用Producer）</code></p>
<p><code></code> <code>* Author:菩提树下的杨过 http://yjmyzz.cnblogs.com</code></p>
<p><code></code> <code>*/</code></p>
<p><code>public</code> <code>class</code> <code>ActiveMQProducer {</code></p>
<p><code></code> <code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code> <code>throws</code> <code>JMSException, IOException, InterruptedException {</code></p>
<p><code></code> <code>ApplicationContext context =</code> <code>new</code> <code>ClassPathXmlApplicationContext(</code> <code>&quot;spring-context.xml&quot;</code> <code>);</code></p>
<p><code></code> <code>PooledConnectionFactory connectionFactory =
context.getBean(PooledConnectionFactory.</code> <code>class</code> <code>);</code></p>
<p><code></code> <code>ActiveMQQueue destination = context.getBean(ActiveMQQueue.</code> <code>class</code> <code>);</code></p>
<p><code></code> <code>Connection connection = connectionFactory.createConnection();</code></p>
<p><code></code> <code>connection.start();</code></p>
<p><code></code> <code>Session session = connection.createSession(</code> <code>false</code> <code>,
Session.AUTO_ACKNOWLEDGE);</code></p>
<p><code></code> <code>MessageProducer producer = session.createProducer(destination);</code></p>
<p><code></code> <code>System.out.println(</code> <code>&quot;准备发送消息...&quot;</code> <code>);</code></p>
<p><code></code> <code>int</code> <code>max =</code> <code>100000</code> <code>;</code></p>
<p><code></code> <code>Long start = System.currentTimeMillis();</code></p>
<p><code></code> <code>for</code> <code>(</code> <code>int</code> <code>i =</code> <code>0</code> <code>; i &lt; max; i++) {</code></p>
<p><code></code> <code>TextMessage msg = session.createTextMessage(</code> <code>&quot;message test:&quot;</code> <code>\+
i);</code></p>
<p><code></code> <code>//msg.setIntProperty(&quot;id&quot;, i);</code></p>
<p><code></code> <code>producer.send(msg);</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>Long end = System.currentTimeMillis();</code></p>
<p><code></code> <code>Long elapse = end - start;</code></p>
<p><code></code> <code>int</code> <code>perform = Double.valueOf(max / (elapse / 1000d)).intValue();</code></p>
<p><code></code> <code>System.out.print(</code> <code>&quot;发送 &quot;</code> <code>\+ max +</code> <code>&quot; 条消息，耗时：&quot;</code> <code>\+ elapse +</code> <code>&quot;毫秒，平均&quot;</code> <code>\+ perform +</code> <code>&quot;条/秒&quot;</code> <code>);</code></p>
<p><code></code> <code>//producer.send(session.createTextMessage(&quot;SHUTDOWN&quot;));</code></p>
<p><code></code> <code>//Thread.sleep(1000 * 3);</code></p>
<p><code></code> <code>//connection.close();</code></p>
<p><code></code> <code>System.exit(</code> <code>0</code> <code>);</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>这二种方式在性能上差不多，4核8G的mac book<br>pro上，大致每秒可以写入3k+条消息。但是从代码量来讲，明显JmsTemplate的代码量更少，推荐使用。</p>
<p>4.3  消费者代码</p>
<p>当然也可以用JmsTemplate接收消息，但是一般得自己去写while(true)循环，而且默认情况下，上下文如果不是同一个连接，JmsTemplate<br>A发出的消息，JmsTemplate<br>B是接收不到的，所以不建议这种方式。最好参考下面的示例，使用JMS的MessageLisenter去监听消息，这也是JMS规范建议的标准做法：</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>38</p>
<p>39</p>
<p>40</p>
<p>41</p>
<p>42</p>
<p><code>package</code> <code>com.cnblogs.yjmyzz.activemq;</code></p>
<p><code>import</code> <code>org.apache.activemq.command.ActiveMQQueue;</code></p>
<p><code>import</code> <code>org.apache.activemq.pool.PooledConnectionFactory;</code></p>
<p><code>import</code> <code>org.springframework.context.ApplicationContext;</code></p>
<p><code>import</code> <code>org.springframework.context.support.ClassPathXmlApplicationContext;</code></p>
<p><code>import</code> <code>javax.jms.*;</code></p>
<p><code>import</code> <code>java.io.IOException;</code></p>
<p><code>/**</code></p>
<p><code></code> <code>* ActiveMQ消息接收示例(使用MessageListener)</code></p>
<p><code></code> <code>* Author:菩提树下的杨过 http://yjmyzz.cnblogs.com</code></p>
<p><code></code> <code>*/</code></p>
<p><code>public</code> <code>class</code> <code>MessageListenerConsumer {</code></p>
<p><code></code> <code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code> <code>throws</code> <code>JMSException, IOException {</code></p>
<p><code></code> <code>ApplicationContext context =</code> <code>new</code> <code>ClassPathXmlApplicationContext(</code> <code>&quot;spring-context.xml&quot;</code> <code>);</code></p>
<p><code></code> <code>PooledConnectionFactory connectionFactory =
context.getBean(PooledConnectionFactory.</code> <code>class</code> <code>);</code></p>
<p><code></code> <code>ActiveMQQueue destination = context.getBean(ActiveMQQueue.</code> <code>class</code> <code>);</code></p>
<p><code></code> <code>Connection connection = connectionFactory.createConnection();</code></p>
<p><code></code> <code>connection.start();</code></p>
<p><code></code> <code>Session session = connection.createSession(</code> <code>false</code> <code>,
Session.AUTO_ACKNOWLEDGE);</code></p>
<p><code></code> <code>MessageConsumer consumer = session.createConsumer(destination);</code></p>
<p><code></code> <code>consumer.setMessageListener(</code> <code>new</code> <code>ActiveMQListener());</code></p>
<p><code></code> <code>System.in.read();</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>static</code> <code>class</code> <code>ActiveMQListener</code> <code>implements</code> <code>MessageListener
{</code></p>
<p><code></code> <code>@Override</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>onMessage(Message message) {</code></p>
<p><code></code> <code>try</code> <code>{</code></p>
<p><code></code> <code>if</code> <code>(message</code> <code>instanceof</code> <code>TextMessage) {</code></p>
<p><code></code> <code>System.out.println(((TextMessage) message).getText());</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code> <code>catch</code> <code>(JMSException e) {</code></p>
<p><code></code> <code>e.printStackTrace();</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>4.4  嵌入式Broker</p>
<p>类似jetty、tombat之类可以内嵌到代码中启动一样，ActiveMQ也可以直接在代码中内嵌启动，这个很方便一些轻量级的使用场景，示例代码如下：</p>
<p>+ View Code  ?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p><code>public</code> <code>class</code> <code>EmbbedBroker {</code></p>
<p><code></code> <code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code> <code>throws</code> <code>Exception {</code></p>
<p><code></code> <code>BrokerService broker =</code> <code>new</code> <code>BrokerService();</code></p>
<p><code></code> <code>broker.addConnector(</code> <code>&quot;tcp://localhost:61616&quot;</code> <code>);</code></p>
<p><code></code> <code>broker.start();</code></p>
<p><code></code> <code>System.out.println(</code> <code>&quot;ActiveMQ 已启动!&quot;</code> <code>);</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>
<p>关于嵌入式Broker的更多细节，可以参考 <a href="http://activemq.apache.org/how-do-i-embed-a-broker-
inside-a-connection.html" target="_blank" rel="noopener"> http://activemq.apache.org/how-do-i-embed-a-broker-<br>inside-a-connection.html </a></p>
<p>4.5  消息的自动确认与手动确认</p>
<p>在接收消息时，如果Session使用的是 Session.AUTO_ACKNOWLEDGE，即：</p>
<p>?</p>
<p>1</p>
<p><code>Session session = connection.createSession(</code> <code>false</code> <code>,
Session.AUTO_ACKNOWLEDGE);</code></p>
<p>则消息一旦被接受，不论onMessage()里的业务逻辑执行成功与否，消息都将从ActiveMQ的队列里立刻删除。如果希望业务处理成功后，再通知Active<br>MQ删除消息，可以改成：</p>
<p>?</p>
<p>1</p>
<p><code>Session session = connection.createSession(</code> <code>false</code> <code>,
Session.CLIENT_ACKNOWLEDGE);</code></p>
<p>然后onMessage方法调用message.acknowledge手动确认，参考以下代码：</p>
<p>?</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p><code>static</code> <code>class</code> <code>ActiveMQListener</code> <code>implements</code> <code>MessageListener {</code></p>
<p><code></code> <code>@Override</code></p>
<p><code></code> <code>public</code> <code>void</code> <code>onMessage(Message message) {</code></p>
<p><code></code> <code>try</code> <code>{</code></p>
<p><code></code> <code>if</code> <code>(message</code> <code>instanceof</code> <code>TextMessage) {</code></p>
<p><code></code> <code>System.out.println(((TextMessage) message).getText());</code></p>
<p><code></code> <code>message.acknowledge();</code> <code>//手动确认消息</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code> <code>catch</code> <code>(JMSException e) {</code></p>
<p><code></code> <code>e.printStackTrace();</code></p>
<p><code></code> <code>}</code></p>
<p><code></code> <code>}</code></p>
<p><code>}</code></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/ActiveMQ笔记(1)：编译、安装、示例代码/#comments" id="sourceId::2018/04/20/ActiveMQ笔记(1)：编译、安装、示例代码/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-40个Java多线程问题总结" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/40个Java多线程问题总结/">40个Java多线程问题总结</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/40个Java多线程问题总结/">
            <time datetime="2018-04-19T16:34:21.408Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/J2EE/">J2EE</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/java/">java</a>, <a class="tag-link" href="/tags/多线程/">多线程</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p><strong> 前言 </strong></p>
<p>个人认为，学习，内容越多、越杂的知识，越需要进行深刻的总结，这样才能记忆深刻，将知识变成自己的。这篇文章主要是对多线程的问题进行总结的，因此罗列了40个多线<br>程的问题。</p>
<p>这些多线程的问题，有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过，但是本文写作的重心就<br>是  <strong> 所有的问题都会按照自己的理解回答一遍，不会去看网上的答案 </strong> ，因此可能有些问题讲的不对，能指正的希望大家不吝指教。</p>
<p><strong> 40个问题汇总 </strong></p>
<p><strong> 1、多线程有什么用？  </strong></p>
<p>一个可能在很多人看来很扯淡的一个问题：我会用多线程就好了，还管它有什么用？在我看来，这个回答更扯淡。所谓”知其然知其所以然”，”会用”只是”知其然”，”为什<br>么用”才是”知其所以然”，只有达到”知其然知其所以然”的程度才可以说是把一个知识点运用自如。OK，下面说说我对这个问题的看法：</p>
<p>（1）发挥多核CPU的优势</p>
<p>随着工业的进步，现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的，4核、8核甚至16核的也都不少见，如果是单线程的程序，那么在双核CPU上就浪费了5<br>0%，在4核CPU上就浪费了75%。 <strong><br>单核CPU上所谓的”多线程”那是假的多线程，同一时间处理器只会处理一段逻辑，只不过线程之间切换得比较快，看着像多个线程”同时”运行罢了  </strong><br>。多核CPU上的多线程才是真正的多线程，它能让你的多段逻辑同时工作，多线程，可以真正发挥出多核CPU的优势来，达到充分利用CPU的目的。</p>
<p>（2）防止阻塞</p>
<p>从程序运行效率的角度来看，单核CPU不但不会发挥出多线程的优势，反而会因为在单核CPU上运行多线程导致线程上下文的切换，而降低程序整体的效率。但是单核CPU<br>我们还是要应用多线程，就是为了防止阻塞。试想，如果单核CPU使用单线程，那么只要这个线程阻塞了，比方说远程读取某个数据吧，对端迟迟未返回又没有设置超时时间，<br>那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题，多条线程同时运行，哪怕一条线程的代码执行读取数据阻塞，也不会影响其它任务的执行。</p>
<p>（3）便于建模</p>
<p>这是另外一个没有这么明显的优点了。假设有一个大的任务A，单线程编程，那么就要考虑很多，建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务，任<br>务B、任务C、任务D，分别建立程序模型，并通过多线程分别运行这几个任务，那就简单很多了。</p>
<p><strong> 2、创建线程的方式  </strong></p>
<p>比较常见的一个问题了，一般就是两种：</p>
<p>（1）继承Thread类</p>
<p>（2）实现Runnable接口</p>
<p>至于哪个好，不用说肯定是后者好，因为实现接口的方式比继承类的方式更灵活，也能减少程序之间的耦合度，  <strong> 面向接口编程 </strong> 也是设计模式6大原则的核心。</p>
<p><strong> 3、start()方法和run()方法的区别 </strong></p>
<p>只有调用了start()方法，才会表现出多线程的特性，不同线程的run()方法里面的代码交替执行。如果只是调用run()方法，那么代码还是同步执行的，必须等<br>待一个线程的run()方法里面的代码全部执行完毕之后，另外一个线程才可以执行其run()方法里面的代码。</p>
<p><strong> 4、Runnable接口和Callable接口的区别  </strong></p>
<p>有点深的问题了，也看出一个Java程序员学习知识的广度。</p>
<p>Runnable接口中的run()方法的返回值是void，它做的事情只是纯粹地去执行run()方法中的代码而已；Callable接口中的call()方法是有<br>返回值的，是一个泛型，和Future、FutureTask配合可以用来获取异步执行的结果。</p>
<p>这其实是很有用的一个特性，因为  <strong> 多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性 </strong> ，某条线程是否执行了？某条线程执行了多久<br>？某条线程执行的时候我们期望的数据是否已经赋值完毕？无法得知，我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/Future<br>Task却可以获取多线程运行的结果，可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务，真的是非常有用。</p>
<p><strong> 5、CyclicBarrier和CountDownLatch的区别 </strong></p>
<p>两个看上去有点像的类，都在java.util.concurrent下，都可以用来表示代码运行到某个点上，二者的区别在于：</p>
<p>（1）CyclicBarrier的某个线程运行到某个点上之后，该线程即停止运行，直到所有的线程都到达了这个点，所有线程才重新运行；CountDownLatc<br>h则不是，某线程运行到某个点上之后，只是给某个数值-1而已，该线程继续运行</p>
<p>（2）CyclicBarrier只能唤起一个任务，CountDownLatch可以唤起多个任务</p>
<p>（3）CyclicBarrier可重用，CountDownLatch不可重用，计数值为0该CountDownLatch就不可再用了</p>
<p><strong> 6、volatile关键字的作用  </strong></p>
<p>一个非常重要的问题，是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型，这里就不讲Java内<br>存模型了，可以参见第31点，volatile关键字的作用主要有两个：</p>
<p>（1）多线程主要围绕可见性和原子性两个特性而展开，使用volatile关键字修饰的变量，保证了其在多线程之间的可见性，即每次读取到volatile变量，一定<br>是最新的数据</p>
<p>（2）代码底层执行不像我们看到的高级语言—-Java程序这么简单，它的执行是  <strong><br>Java代码–&gt;字节码–&gt;根据字节码执行对应的C/C++代码–&gt;C/C++代码被编译成汇编语言–&gt;和硬件电路交互 </strong> ，现实中，为了获取更好的性能JVM可<br>能会对指令进行重排序，多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序，当然这也一定程度上降低了代码执行效率</p>
<p>从实践角度而言，volatile的一个重要作用就是和CAS结合，保证了原子性，详细的可以参见java.util.concurrent.atomic包下的类，<br>比如AtomicInteger。</p>
<p><strong> 7、什么是线程安全  </strong></p>
<p>又是一个理论的问题，各式各样的答案有很多，我给出一个个人认为解释地最好的：  <strong><br>如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果，那么你的代码就是线程安全的 </strong> 。</p>
<p>这个问题有值得一提的地方，就是线程安全也是有几个级别的：</p>
<p>（1）不可变</p>
<p>像String、Integer、Long这些，都是final类型的类，任何一个线程都改变不了它们的值，要改变除非新创建一个，因此这些不可变对象不需要任何同步<br>手段就可以直接在多线程环境下使用</p>
<p>（2）绝对线程安全</p>
<p>不管运行时环境如何，调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价，Java中标注自己是线程安全的类，实际上绝大多数都不是线程安全的，<br>不过绝对线程安全的类，Java中也有，比方说CopyOnWriteArrayList、CopyOnWriteArraySet</p>
<p>（3）相对线程安全</p>
<p>相对线程安全也就是我们通常意义上所说的线程安全，像Vector这种，add、remove方法都是原子操作，不会被打断，但也仅限于此，如果有个线程在遍历某个V<br>ector、有个线程同时在add这个Vector，99%的情况下都会出现ConcurrentModificationException，也就是  <strong><br>fail-fast机制 </strong> 。</p>
<p>（4）线程非安全</p>
<p>这个就没什么好说的了，ArrayList、LinkedList、HashMap等都是线程非安全的类</p>
<p><strong> 8、Java中如何获取到线程dump文件  </strong></p>
<p>死循环、死锁、阻塞、页面打开慢等问题，打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈，获取到线程堆栈有两步：</p>
<p>（1）获取到线程的pid，可以通过使用jps命令，在Linux环境下还可以使用ps -ef | grep java</p>
<p>（2）打印线程堆栈，可以通过使用jstack pid命令，在Linux环境下还可以使用kill -3 pid</p>
<p>另外提一点，Thread类提供了一个<br>getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法，因此此方法是和具体线程实例绑定的，每次获取获取到的是具体某个线程当前运行的堆栈，</p>
<p><strong> 9、一个线程如果出现了运行时异常会怎么样  </strong></p>
<p>如果这个异常没有被捕获的话，这个线程就停止执行了。另外重要的一点是：  <strong> 如果这个线程持有某个某个对象的监视器，那么这个对象监视器会被立即释放 </strong></p>
<p><strong> 10、如何在两个线程之间共享数据  </strong></p>
<p>通过在线程之间共享对象就可以了，然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待，比方说阻塞队<br>列BlockingQueue就是为线程之间共享数据而设计的</p>
<p><strong> 11、sleep方法和wait方法有什么区别 </strong></p>
<p>这个问题常问，sleep方法和wait方法都可以用来放弃CPU一定的时间，不同点在于如果线程持有某个对象的监视器，sleep方法不会放弃这个对象的监视器，w<br>ait方法会放弃这个对象的监视器</p>
<p><strong> 12、生产者消费者模型的作用是什么  </strong></p>
<p>这个问题很理论，但是很重要：</p>
<p>（1）  <strong> 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率 </strong> ，这是生产者消费者模型最重要的作用</p>
<p>（2）解耦，这是生产者消费者模型附带的作用，解耦意味着生产者和消费者之间的联系少，联系越少越可以独自发展而不需要收到相互的制约</p>
<p><strong> 13、ThreadLocal有什么用 </strong></p>
<p>简单说ThreadLocal就是一种以  <strong> 空间换时间 </strong> 的做法，在每个Thread里面维护了一个以开地址法实现的ThreadLocal.Threa<br>dLocalMap，把数据进行隔离，数据不共享，自然就没有线程安全方面的问题了</p>
<p><strong> 14、为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用  </strong></p>
<p>这是JDK强制的，wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁</p>
<p><strong> 15、wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别  </strong></p>
<p>wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于：  <strong><br>wait()方法立即释放对象监视器，notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器 </strong> 。</p>
<p><strong> 16、为什么要使用线程池 </strong>   </p>
<p>避免频繁地创建和销毁线程，达到线程对象的重用。另外，使用线程池还可以根据项目灵活地控制并发的数目。</p>
<p><strong> 17、怎么检测一个线程是否持有对象监视器  </strong></p>
<p>我也是在网上看到一道多线程面试题才知道有方法可以判断某个线程是否持有对象监视器：Thread类提供了一个holdsLock(Object<br>obj)方法，当且仅当对象obj的监视器被某条线程持有的时候才会返回true，注意这是一个static方法，这意味着  <strong> “某条线程”指的是当前线程
</strong> 。</p>
<p><strong> 18、synchronized和ReentrantLock的区别  </strong></p>
<p>synchronized是和if、else、for、while一样的关键字，ReentrantLock是类，这是二者的本质区别。既然ReentrantLoc<br>k是类，那么它就提供了比synchronized更多更灵活的特性，可以被继承、可以有方法、可以有各种各样的类变量，ReentrantLock比synchro<br>nized的扩展性体现在几点上：</p>
<p>（1）ReentrantLock可以对获取锁的等待时间进行设置，这样就避免了死锁</p>
<p>（2）ReentrantLock可以获取各种锁的信息</p>
<p>（3）ReentrantLock可以灵活地实现多路通知</p>
<p>另外，二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁，synchronized操作的应该是对象头中mar<br>k word，这点我不能确定。</p>
<p><strong> 19、ConcurrentHashMap的并发度是什么  </strong></p>
<p>ConcurrentHashMap的并发度就是segment的大小，默认为16，这意味着最多同时可以有16条线程操作ConcurrentHashMap，这也<br>是ConcurrentHashMap对Hashtable的最大优势，任何情况下，Hashtable能同时有两条线程获取Hashtable中的数据吗？</p>
<p><strong> 20、ReadWriteLock是什么  </strong></p>
<p>首先明确一下，不是说ReentrantLock不好，只是ReentrantLock某些时候有局限。如果使用ReentrantLock，可能本身是为了防止线程<br>A在写数据、线程B在读数据造成的数据不一致，但这样，如果线程C在读数据、线程D也在读数据，读数据是不会改变数据的，没有必要加锁，但是还是加锁了，降低了程序的<br>性能。</p>
<p>因为这个，才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口，ReentrantReadWriteLock是ReadWri<br>teLock接口的一个具体实现，实现了读写的分离，  <strong> 读锁是共享的，写锁是独占的 </strong><br>，读和读之间不会互斥，读和写、写和读、写和写之间才会互斥，提升了读写的性能。</p>
<p><strong> 21、FutureTask是什么  </strong></p>
<p>这个其实前面有提到过，FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类，可以对这个异步运算的任务<br>的结果进行等待获取、判断是否已经完成、取消任务等操作。当然，由于FutureTask也是Runnable接口的实现类，所以FutureTask也可以放入线程<br>池中。</p>
<p><strong> 22、Linux环境下如何查找哪个线程使用CPU最长  </strong></p>
<p>这是一个比较偏实践的问题，这种问题我觉得挺有意义的。可以这么做：</p>
<p>（1）获取项目的pid，jps或者ps -ef | grep java，这个前面有讲过</p>
<p>（2）top -H -p pid，顺序不能改变</p>
<p>这样就可以打印出当前的项目，每条线程占用CPU时间的百分比。注意这里打出的是LWP，也就是操作系统原生线程的线程号，我笔记本山没有部署Linux环境下的Ja<br>va工程，因此没有办法截图演示，网友朋友们如果公司是使用Linux环境部署项目的话，可以尝试一下。</p>
<p>使用”top -H -p pid”+”jps<br>pid”可以很容易地找到某条占用CPU高的线程的线程堆栈，从而定位占用CPU高的原因，一般是因为不当的代码操作导致了死循环。</p>
<p>最后提一点，”top -H -p pid”打出来的LWP是十进制的，”jps<br>pid”打出来的本地线程号是十六进制的，转换一下，就能定位到占用CPU高的线程的当前线程堆栈了。</p>
<p><strong> 23、Java编程写一个会导致死锁的程序  </strong></p>
<p>第一次看到这个题目，觉得这是一个非常好的问题。很多人都知道死锁是怎么一回事儿：线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。当然也仅限于此了，问<br>一下怎么写一个死锁的程序就不知道了，这种情况说白了就是不懂什么是死锁，懂一个理论就完事儿了，实践中碰到死锁的问题基本上是看不出来的。</p>
<p>真正理解什么是死锁，这个问题其实不难，几个步骤：</p>
<p>（1）两个线程里面分别持有两个Object对象：lock1和lock2。这两个lock作为同步代码块的锁；</p>
<p>（2）线程1的run()方法中同步代码块先获取lock1的对象锁，Thread.sleep(xxx)，时间不需要太多，50毫秒差不多了，然后接着获取lock<br>2的对象锁。这么做主要是为了防止线程1启动一下子就连续获得了lock1和lock2两个对象的对象锁</p>
<p>（3）线程2的run)(方法中同步代码块先获取lock2的对象锁，接着获取lock1的对象锁，当然这时lock1的对象锁已经被线程1锁持有，线程2肯定是要等<br>待线程1释放lock1的对象锁的</p>
<p>这样，线程1”睡觉”睡完，线程2已经获取了lock2的对象锁了，线程1此时尝试获取lock2的对象锁，便被阻塞，此时一个死锁就形成了。代码就不写了，占的篇幅<br>有点多， <a href="http://www.cnblogs.com/xrq730/p/4853713.html" target="_blank" rel="noopener"> Java多线程7：死锁 </a><br>这篇文章里面有，就是上面步骤的代码实现。</p>
<p><strong> 24、怎么唤醒一个阻塞的线程  </strong></p>
<p>如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞，可以中断线程，并且通过抛出InterruptedException来唤醒它<br>；如果线程遇到了IO阻塞，无能为力，因为IO是操作系统实现的，Java代码并没有办法直接接触到操作系统。</p>
<p><strong> 25、不可变对象对多线程有什么帮助 </strong></p>
<p>前面有提到过的一个问题，不可变对象保证了对象的内存可见性，对不可变对象的读取不需要进行额外的同步手段，提升了代码执行效率。</p>
<p><strong> 26、什么是多线程的上下文切换  </strong></p>
<p>多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。</p>
<p><strong> 27、如果你提交任务时，线程池队列已满，这时会发生什么  </strong></p>
<p>这里区分一下：</p>
<ol>
<li>如果使用的是无界队列LinkedBlockingQueue，也就是无界队列的话，没关系，继续添加任务到阻塞队列中等待执行，因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列，可以无限存放任务 </li>
<li>如果使用的是有界队列比如ArrayBlockingQueue，任务首先会被添加到ArrayBlockingQueue中，ArrayBlockingQueue满了，会根据maximumPoolSize的值增加线程数量，如果增加了线程数量还是处理不过来，ArrayBlockingQueue继续满，那么则会使用拒绝策略RejectedExecutionHandler处理满了的任务，默认是AbortPolicy </li>
</ol>
<p><strong> 28、Java中用到的线程调度算法是什么  </strong></p>
<p>抢占式。一个线程用完CPU之后，操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。</p>
<p><strong> 29、Thread.sleep(0)的作用是什么  </strong></p>
<p>这个问题和上面那个问题是相关的，我就连在一起了。由于Java采用抢占式的线程调度算法，因此可能会出现某条线程常常获取到CPU控制权的情况，为了让某些优先级比<br>较低的线程也能获取到CPU控制权，可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作，这也是平衡CPU控制权的一种操作。</p>
<p><strong> 30、什么是自旋  </strong></p>
<p>很多synchronized里面的代码只是一些很简单的代码，执行时间非常快，此时等待的线程都加锁可能是一种不太值得的操作，因为线程阻塞涉及到用户态和内核态切<br>换的问题。既然synchronized里面的代码执行得非常快，不妨让等待锁的线程不要被阻塞，而是在synchronized的边界做忙循环，这就是自旋。如果做<br>了多次忙循环发现还没有获得锁，再阻塞，这样可能是一种更好的策略。</p>
<p><strong> 31、什么是Java内存模型  </strong></p>
<p>Java内存模型定义了一种多线程访问Java内存的规范。Java内存模型要完整讲不是这里几句话能说清楚的，我简单总结一下Java内存模型的几部分内容：</p>
<p>（1）Java内存模型将内存分为了  <strong> 主内存和工作内存 </strong> 。类的状态，也就是类之间共享的变量，是存储在主内存中的，每次Java线程用到这些主内存中<br>的变量的时候，会读一次主内存中的变量，并让这些内存在自己的工作内存中有一份拷贝，运行自己线程代码的时候，用到这些变量，操作的都是自己工作内存中的那一份。在线<br>程代码执行完毕之后，会将最新的值更新到主内存中去</p>
<p>（2）定义了几个原子操作，用于操作主内存和工作内存中的变量</p>
<p>（3）定义了volatile变量的使用规则</p>
<p>（4）happens-before，即先行发生原则，定义了操作A必然先行发生于操作B的一些规则，比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的<br>代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等，只要符合这些规则，则不需要额外做同步措施<br>，如果某段代码不符合所有的happens-before规则，则这段代码一定是线程非安全的</p>
<p><strong> 32、什么是CAS </strong>   </p>
<p>CAS，全称为Compare and Swap，即比较-替换。假设有三个操作数：  <strong><br>内存值V、旧的预期值A、要修改的值B，当且仅当预期值A和内存值V相同时，才会将内存值修改为B并返回true，否则什么都不做并返回false </strong> 。当然CA<br>S一定要volatile变量配合，这样才能保证每次拿到的变量是主内存中最新的那个值，否则旧的预期值A对某条线程来说，永远是一个不会变的值A，只要某次CAS操<br>作失败，永远都不可能成功。</p>
<p><strong> 33、什么是乐观锁和悲观锁  </strong></p>
<p>（1）乐观锁：就像它的名字一样，对于并发间操作产生的线程安全问题持乐观状态，乐观锁认为竞争不总是会发生，因此它不需要持有锁，将  <strong> 比较-替换 </strong><br>这两个动作作为一个原子操作尝试去修改内存中的变量，如果失败则表示发生冲突，那么就应该有相应的重试逻辑。</p>
<p>（2）悲观锁：还是像它的名字一样，对于并发间操作产生的线程安全问题持悲观状态，悲观锁认为竞争总是会发生，因此每次对某资源进行操作时，都会持有一个独占的锁，就<br>像synchronized，不管三七二十一，直接上了锁就操作资源了。</p>
<p><strong> 34、什么是AQS  </strong></p>
<p>简单说一下AQS，AQS全称为AbstractQueuedSychronizer，翻译过来应该是抽象队列同步器。</p>
<p>如果说java.util.concurrent的基础是CAS的话，那么AQS就是整个Java并发包的核心了，ReentrantLock、CountDownL<br>atch、Semaphore等等都用到了它。AQS实际上以双向队列的形式连接所有的Entry，比方说ReentrantLock，所有等待的线程都被放在一个E<br>ntry中并连成双向队列，前面一个线程使用ReentrantLock好了，则双向队列实际上的第一个Entry开始运行。</p>
<p>AQS定义了对双向队列所有的操作，而只开放了tryLock和tryRelease方法给开发者使用，开发者可以根据自己的实现重写tryLock和tryRele<br>ase方法，以实现自己的并发功能。</p>
<p><strong> 35、单例模式的线程安全性  </strong></p>
<p>老生常谈的问题了，首先要说的是单例模式的线程安全意味着：  <strong> 某个类的实例在多线程环境下只会被创建一次出来 </strong> 。单例模式有很多种的写法，我总结一下：</p>
<p>（1）饿汉式单例模式的写法：线程安全</p>
<p>（2）懒汉式单例模式的写法：非线程安全</p>
<p>（3）双检锁单例模式的写法：线程安全</p>
<p><strong> 36、Semaphore有什么作用 </strong></p>
<p>Semaphore就是一个信号量，它的作用是 <strong> 限制某段代码块的并发数  </strong> 。Semaphore有一个构造函数，可以传入一个int型整数n，表示某段<br>代码最多只有n个线程可以访问，如果超出了n，那么请等待，等到某个线程执行完毕这段代码块，下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的<br>int型整数n=1，相当于变成了一个synchronized了。</p>
<p><strong> 37、Hashtable的size()方法中明明只有一条语句”return count”，为什么还要做同步？  </strong></p>
<p>这是我之前的一个困惑，不知道大家有没有想过这个问题。某个方法中如果有多条语句，并且都在操作同一个类变量，那么在多线程环境下不加锁，势必会引发线程安全问题，这<br>很好理解，但是size()方法明明只有一条语句，为什么还要加锁？</p>
<p>关于这个问题，在慢慢地工作、学习中，有了理解，主要原因有两点：</p>
<p>（1）  <strong> 同一时间只能有一条线程执行固定类的同步方法，但是对于类的非同步方法，可以多条线程同时访问 </strong> 。所以，这样就有问题了，可能线程A在执行Ha<br>shtable的put方法添加数据，线程B则可以正常调用size()方法读取Hashtable中当前元素的个数，那读取到的值可能不是最新的，可能线程A添加了<br>完了数据，但是没有对size++，线程B就已经读取size了，那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后，意味着线程<br>B调用size()方法只有在线程A调用put方法完毕之后才可以调用，这样就保证了线程安全性</p>
<p>（2）  <strong> CPU执行代码，执行的不是Java代码，这点很关键，一定得记住 </strong><br>。Java代码最终是被翻译成汇编代码执行的，汇编代码才是真正可以和硬件电路交互的代码。  <strong><br>即使你看到Java代码只有一行，甚至你看到Java代码编译之后生成的字节码也只有一行，也不意味着对于底层来说这句语句的操作只有一个 </strong><br>。一句”return count”假设被翻译成了三句汇编语句执行，完全可能执行完第一句，线程就切换了。</p>
<p><strong> 38、线程类的构造方法、静态块是被哪个线程调用的  </strong></p>
<p>这是一个非常刁钻和狡猾的问题。请记住：线程类的构造方法、静态块是被new这个线程类所在的线程所调用的，而run方法里面的代码才是被线程自身所调用的。</p>
<p>如果说上面的说法让你感到困惑，那么我举个例子，  假设Thread2中new了Thread1，main函数中new了Thread2，那么：</p>
<p>（1）Thread2的构造方法、静态块是main线程调用的，Thread2的run()方法是Thread2自己调用的</p>
<p>（2）Thread1的构造方法、静态块是Thread2调用的，Thread1的run()方法是Thread1自己调用的</p>
<p><strong> 39、同步方法和同步块，哪个是更好的选择  </strong></p>
<p>同步块，这意味着同步块之外的代码是异步执行的，这比同步整个方法更提升代码的效率。请知道一条原则：  <strong> 同步的范围越小越好 </strong> 。</p>
<p>借着这一条，我额外提一点，虽说同步的范围越少越好，但是在Java虚拟机中还是存在着一种叫做  <strong> 锁粗化 </strong> 的优化方法，这种方法就是把同步范围变大。这<br>是有用的，比方说StringBuffer，它是一个线程安全的类，自然最常用的append()方法是一个同步方法，我们写代码的时候会反复append字符串，这<br>意味着要进行反复的加锁-&gt;解锁，这对性能不利，因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换，因此Java虚拟机会将多次appe<br>nd方法调用的代码进行一个锁粗化的操作，将多次的append的操作扩展到append方法的头尾，变成一个大的同步块，这样就减少了加锁–&gt;解锁的次数，有效地提<br>升了代码执行的效率。</p>
<p><strong> 40、高并发、任务执行时间短的业务怎样使用线程池？并发不高、任务执行时间长的业务怎样使用线程池？并发高、业务执行时间长的业务怎样使用线程池？  </strong></p>
<p>这是我在并发编程网上看到的一个问题，把这个问题放在最后一个，希望每个人都能看到并且思考一下，因为这个问题非常好、非常实际、非常专业。关于这个问题，个人看法是<br>：</p>
<p>（1）高并发、任务执行时间短的业务，线程池线程数可以设置为CPU核数+1，减少线程上下文的切换</p>
<p>（2）并发不高、任务执行时间长的业务要区分开看：</p>
<p>a）假如是业务时间长集中在IO操作上，也就是IO密集型的任务，因为IO操作并不占用CPU，所以不要让所有的CPU闲下来，可以加大线程池中的线程数目，让CPU<br>处理更多的业务</p>
<p>b）假如是业务时间长集中在计算操作上，也就是计算密集型任务，这个就没办法了，和（1）一样吧，线程池中的线程数设置得少一些，减少线程上下文的切换</p>
<p>（3）并发高、业务执行时间长，解决这种类型任务的关键不在于线程池而在于整体架构的设计，看看这些业务里面某些数据是否能做缓存是第一步，增加服务器是第二步，至于<br>线程池的设置，设置参考（2）。最后，业务执行时间长的问题，也可能需要分析一下，看看能不能使用中间件对任务进行拆分和解耦。</p>
<p>** ==================================================================================   </p>
<p>我不能保证写的每个地方都是对的，但是至少能保证不复制、不黏贴，保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后，希望都能看到自己对于<br>技术、对于生活的态度。  </p>
<p>我相信乔布斯说的，只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力，我可以挑灯夜战、不眠不休；面对困难，我愿意迎难而上、永不退缩。  </p>
<p>** 其实我想说的是，我只是一个程序员，这就是我现在纯粹人生的全部。   </p>
<p>==============================================================================<br>==== <strong> </strong> <strong> </strong></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/40个Java多线程问题总结/#comments" id="sourceId::2018/04/20/40个Java多线程问题总结/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-7个步骤让PC网站自动适配手机网页" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/7个步骤让PC网站自动适配手机网页/">7个步骤让PC网站自动适配手机网页</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/7个步骤让PC网站自动适配手机网页/">
            <time datetime="2018-04-19T16:34:21.408Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/web前端/">web前端</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/优化/">优化</a>, <a class="tag-link" href="/tags/搜索引擎/">搜索引擎</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>传统的网站如何完成向移动设备的快速转型？ 通过移动适配技术可以实现，切图网是国内首家基于web技术服务的公司，而移动适配主要通过底层的web技术开发手段来完<br>成，下面切图网将从技术角度来告诉你通过7个步骤来完成一个PC网站向移动设备的跳跃！</p>
<p>1允许网页宽度自动调整</p>
<p>“自适应网页设计”到底是怎么做到的？其实并不难。</p>
<p>首先，在网页代码的头部，加入一行viewport元标签。</p>
<p>viewport是网页默认的宽度和高度，上面这行代码的意思是，网页宽度默认等于屏幕宽度（width=device-width），原始缩放比例<br>（initial-scale=1）为1.0，即网页初始大小占屏幕面积的100%。</p>
<p>所有主流浏览器都支持这个设置，包括IE9。对于那些老式浏览器（主要是IE6、7、8），需要使用css3-mediaqueries.js。</p>
<p>2、不使用绝对宽度</p>
<p>由于网页会根据屏幕宽度调整布局，所以不能使用绝对宽度的布局，也不能使用具有绝对宽度的元素。这一条非常重要。</p>
<p>具体说，CSS代码不能指定像素宽度：</p>
<p>width:xxxpx;</p>
<p>只能指定百分比宽度：</p>
<p>width:xx%;</p>
<p>或者</p>
<p>width:auto;</p>
<p>3、相对大小的字体</p>
<p>字体也不能使用绝对大小（px），而只能使用相对大小（em）。</p>
<p>body{</p>
<p>font:normal100%Helvetica,Arial,sans-serif;</p>
<p>}</p>
<p>上面的代码指定，字体大小是页面默认大小的100%，即16像素。</p>
<p>h1{</p>
<p>font-size:1.5em;</p>
<p>}</p>
<p>然后，h1的大小是默认大小的1.5倍，即24像素（24/16=1.5）。</p>
<p>small{</p>
<p>font-size:0.875em;</p>
<p>}</p>
<p>small元素的大小是默认大小的0.875倍，即14像素（14/16=0.875）。</p>
<p>4、流动布局（fluidgrid）</p>
<p>“流动布局”的含义是，各个区块的位置都是浮动的，不是固定不变的。</p>
<p>.main{</p>
<p>float:right;</p>
<p>width:70%;</p>
<p>}</p>
<p>.leftBar{</p>
<p>float:left;</p>
<p>width:25%;</p>
<p>}</p>
<p>float的好处是，如果宽度太小，放不下两个元素，后面的元素会自动滚动到前面元素的下方，不会在水平方向overflow（溢出），避免了水平滚动条的出现。</p>
<p>另外，绝对定位（position:absolute）的使用，也要非常小心。</p>
<p>5、选择加载CSS</p>
<p>“自适应网页设计”的核心，就是CSS3引入的MediaQuery模块。</p>
<p>它的意思就是，自动探测屏幕宽度，然后加载相应的CSS文件。</p>
<p>media=”screenand(max-device-width:400px)”</p>
<p>href=”tinyScreen.css”/&gt;</p>
<p>上面的代码意思是，如果屏幕宽度小于400像素（max-device-width:400px），就加载tinyScreen.css文件。</p>
<p>media=”screenand(min-width:400px)and(max-device-width:600px)”</p>
<p>href=”smallScreen.css”/&gt;</p>
<p>如果屏幕宽度在400像素到600像素之间，则加载smallScreen.css文件。</p>
<p>除了用html标签加载CSS文件，还可以在现有CSS文件中加载。</p>
<p>6、CSS的@media规则</p>
<p>同一个CSS文件中，也可以根据不同的屏幕分辨率，选择应用不同的CSS规则。</p>
<p>@mediascreenand(max-device-width:400px){</p>
<p>.column{</p>
<p>float:none;</p>
<p>width:auto;</p>
<p>}</p>
<p>#sidebar{</p>
<p>display:none;</p>
<p>}</p>
<p>}</p>
<p>上面的代码意思是，如果屏幕宽度小于400像素，则column块取消浮动（float:none）、宽度自动调节（width:auto），sidebar块不显示<br>（display:none）。</p>
<p>7、图片的自适应（fluidimage）</p>
<p>除了布局和文本，”自适应网页设计”还必须实现图片的自动缩放。</p>
<p>这只要一行CSS代码：</p>
<p>img{max-width:100%;}</p>
<p>这行代码对于大多数嵌入网页的视频也有效，所以可以写成：</p>
<p>img,object{max-width:100%;}</p>
<p>老版本的IE不支持max-width，所以只好写成：</p>
<p>img{width:100%;}</p>
<p>此外，windows平台缩放图片时，可能出现图像失真现象。这时，可以尝试使用IE的专有命令：</p>
<p>img{-ms-interpolation-mode:bicubic;}</p>
<p>或者，EthanMarcotte的imgSizer.js。</p>
<p>addLoadEvent(function(){</p>
<p>varimgs=document.getElementById(“content”).getElementsByTagName(“img”);</p>
<p>imgSizer.collate(imgs);</p>
<p>});</p>
<p>最好还是做适配分辨率的图片。有很多方法可以做到同样效果，服务器端和客户端都可以实现。</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/7个步骤让PC网站自动适配手机网页/#comments" id="sourceId::2018/04/20/7个步骤让PC网站自动适配手机网页/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-【译】Redis Cluster官方教程" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/【译】Redis Cluster官方教程/">【译】Redis Cluster官方教程</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/【译】Redis Cluster官方教程/">
            <time datetime="2018-04-19T16:34:21.407Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/Redis/">Redis</a>
    </div>

                        
                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>摘要<br>这篇文章主要介绍redis cluster可用性和一致性相关的特性，值得一提的是，redis cluster需要高于等于redis3.0版本。</p>
<p><strong> 概要 </strong></p>
<p>这篇文章主要介绍redis cluster可用性和一致性相关的特性，值得一提的是，redis cluster需要高于等于redis3.0版本。</p>
<p><strong> Redis Cluster 101 </strong><br>Redis Cluster提供了一种数据自动分片到不同Redis实例中的解决方案，Redis Cluster在一定程度上提高了Redis系统的可用性，即当一<br>些节点挂掉之后，其他节点还可以继续提供服务。而当大部分的master节点都挂掉的时候，Redis Cluster会停止服务。<br>因此Redis Cluster带来了：</p>
<ol>
<li>自动将数据分散到不同的节点。 </li>
<li>允许当一些节点挂掉之后剩下的节点仍然可以工作，或者是禁止这些节点继续工作。 </li>
</ol>
<p><strong> Redis Cluster TCP ports </strong><br>每一个Redis Cluster节点需要开两个TCP端口，一个是用来处理客户端的请求，例如6379端口，另一个端口由第一个端口加上10000之后得到即163<br>79，第二个端口用来作为集群总线，也就是集群之间节点相互进行二进制通信用的。集群总线的用处如下：</p>
<ul>
<li>错误检测 </li>
<li>配置更新 </li>
<li>failover </li>
<li>…… </li>
</ul>
<p>客户端的连接端口和集群总线的连接端口之间的距离10000是固定的。为了让Redis Cluster正常的工作，对于每一个节点：</p>
<ul>
<li>客户端连接端口需要能够和Cluster进行通信，用于keys迁移。 </li>
<li>所有节点的Cluster端口能够相互通信。 </li>
</ul>
<p>如果这两个端口没有打开，则Redis Cluster不能够正常工作。<br>Cluster端口之间使用特殊的二进制协议进行通信，这种二进制协议更适合节点之间通信，可以占用更小的带宽和更少的传输时间。</p>
<p><strong> Redis Cluster Docker </strong><br>当前的Redis Cluster不支持NATted环境，Docker使用端口映射技术，也即运行在Docker容器内部的应用暴露在容器外面的端口和应用暴露的端<br>口不一样，这对于多个应用在Docker中使用同一个端口是非常重要的。为了让Redis Cluster能够在Docker中好好工作，我们需要检查–<br>net=host选项，更多的信息见 <a href="https://docs.docker.com/engine/userguide/networking/" target="_blank" rel="noopener"> Docket文档
</a> 。</p>
<p><strong> Redis Cluster 数据 sharding  </strong><br>Redis Cluster不使用一致性哈希，但是不同形式的sharding，每一个key理论上是hash slot的一部分。在Redis<br>Cluster中有16384个hash slot，为了计算指定key的hash<br>slot，我们简单的使用CRC16算法并对结果进行16384取模。在Redis Cluster中的每一个节点代表着所有hash<br>slot的一个子集，例如我们有三个节点：</p>
<ul>
<li>Node A 包括0到5500之间的hash slots </li>
<li>Node B 包括5501到11000之间的hash slots </li>
<li>Node C 包括11001到16383之间的hash slots </li>
</ul>
<p>这使得可以很容易的向Redis Cluster中添加或删除节点。例如，我们想要添加一个节点D，我们需要将一些hash<br>slots从A，B，C节点移给D。假如我们想要移除A节点，我们可以把A节点上原有的hash slots移动到B和C节点。当A节点的hash<br>slots移除完毕之后，就可以把A节点下线了。<br>将hash slots从一个节点移动到其他节点不需要任何的停机操作。<br>Redis Cluster支持多key操作，多个key是通过一个命令、或者一个事务、或者一个lua脚本执行的，属于同一个hash<br>slot。用户可以强制多个key属于同一个hash slot，这个概念叫hash tags。<br>Hash tags有关的介绍在Redis Cluster规范里面有介绍，主旨是当key中包含{}的时候，只有{}括号内的字符串才被用来hash。例如，一个k<br>ey是this{key}，另一个key是that{key}，他们会被映射到同一个hash slot，而且可以被用于一批key一个命令的操作。</p>
<p><strong> Redis Cluster master-slave模式 </strong><br>为了让当某一个master挂掉之后，这个master的hash slot仍然可以使用，Redis Cluster使用master-<br>slave模式，因此对于每一批hash slot来说都有N个备份，其中N-1个是slave。在我们的例子中，节点A、B、C，如果B挂了，则整个Redis<br>Cluster就不能用了，因为没有实例类服务5501-11000这部分hash<br>slots。但是如果我们为每个master节点各增加一个slave节点，比如A1，B1，C1节点，如果B挂了，整个集群还是可以正常工作的。<br>Node B1是B的复制，如果B挂了，Cluster将会选择B1作为新的master，然后继续工作。值得注意的是，如果B和B1同时挂了，那就没辙了。</p>
<p><strong> Redis Cluster一致性保证 </strong><br>Redis Cluster并不保证强一致性，也就是在一定条件下，Redis Cluster可能丢失被确认写入的数据，第一个有可能Redis<br>Cluster丢失数据的原因是因为复制是异步进行的，意味着下面情景有可能发生：</p>
<ul>
<li>客户端将数据写入B </li>
<li>master B回复客户端写入成功 </li>
<li>master将写命令传播给B1，B2和B3 </li>
</ul>
<p>我们可以看到，在B回复客户端之前不会等到B1，B2，B3的应答。因此如果客户端向B写了数据，B确认了，但是在数据复制到B1，B2，B3之前挂了，那数据对于B<br>1，B2，B3来说就丢失了。和传统的数据库类似，可以强制数据库将日志写入磁盘再告知客户端写成功，但是这么做带来的性能损耗是非常大的，这对于Redis<br>Cluster来说就是同步复制。<br>所以这就是一致性和可用性之间的权衡，如果确实需要，Redis Cluster是支持同步写的，由WAIT命令实现，这可以保证尽量少的丢失数据，尽管Redis<br>Cluster提供了同步复制的支持，但是Redis Cluster并没有实现强一致性，因此在一些复杂的应用场景中，还是有可能丢失数据的。<br>另外一个数据会丢失的场景也值得一提，即当出现网络分区的时候，也就是某一个或多个master和其他的master失去了联系，举个例子，我们有六个Redis实例<br>，三个master和三个slave，还有一个client我们把它叫做Z1。当出现网络分区的时候，比如A，C，A1，B1，C1，处于一个分区，另一个分区有B和<br>Z1。<br>Z1还可以向B写数据，B也接受了Z1的写请求，如果分区只持续一小段时间，Cluster仍然可以继续工作。然而，如果时间足够大，Cluster选举了B1作为m<br>aster，那么之前Z1写入的数据就丢失了。<br>值得一提的是在Z1还可以向B写数据的时候，有一个最大的窗口时间，如果过了这个时间，Cluster选取了B1作为master，B就不能够接受写请求了。这段时间<br>是非常重要的，这个时间被叫做node timeout。当node<br>timeout之后，master被认为出问题了，并且可以被其slave代替，然后master不能够处理写请求。</p>
<p><strong> Redis Cluster配置参数 </strong><br><strong> cluster-enabled&lt;yes/no&gt; </strong> ：如果是yes，则当前redis启动为Redis Cluster的一个实例，否则当前Redis以单机模式启动。   </p>
<p><strong> cluster-config-file<filename> </filename></strong> :值得注意的是这个配置文件是用户不可编辑的，是由Redis 集群节点自动创建的，每次节点状态有变化的时候都会持久化这个配置文件，也是为了Redis集群节点重启的时候重新读取。配置文件的内容包括集群中的其他节点、他们的状态、持久变量等等。每当接受到消息的时候，文件会被刷新并且写入磁盘。   </p>
<p><strong> cluster-node-timeout<milliseconds> </milliseconds></strong> ：Redis集群节点的超时时间，如果在这个时间内Redis集群节点不可达，则集群将会执行failover策略，将该master的slave节点选为master。这个参数控制Redis集群重要的，值得提的是，如果指定时间当前节点联系不到大多数的master，则会拒绝服务。   </p>
<p><strong> cluster-slave-validity-factor<factor> </factor></strong> ：如果将该项设置为0，不管slave节点和master节点间失联多久都会一直尝试failover（设为正数，失联大于一定时间（factor*节点TimeOut），不再进行FailOver）。比如，如果节点的timeout设置为5秒，该项设置为10，如果master跟slave之间失联超过50秒，slave不会去failover它的master（意思是不会去把master设置为挂起状态，并取代它）。注意：任意非0数值都有可能导致当master挂掉又没有slave去failover它，这样redis集群不可用。在这种情况下只有原来那个master重新回到集群中才能让集群恢复工作。   </p>
<p><strong> cluster-migration-barrier<count> </count></strong> ：一个master可以拥有的最小slave数量。该项的作用是，当一个master没有任何slave的时候，某些有富余slave的master节点，可以自动的分一个slave给它。   </p>
<p><strong> cluster-require-full-coverage&lt;yes/no&gt; </strong> ：如果该项设置为yes（默认就是yes） 当一定比例的键空间没有被覆盖到（就是某一部分的哈希槽没了，有可能是暂时挂了）集群就停止处理任何查询操作。如果该项设置为no，那么就算请求中只有一部分的键可以被查到，一样可以查询（但是有可能会查不全） </p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/【译】Redis Cluster官方教程/#comments" id="sourceId::2018/04/20/【译】Redis Cluster官方教程/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <nav id="page-nav">
        <a class="extend prev" rel="prev" href="/page/5/">&laquo; 上一页</a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><span class="page-number current">6</span><a class="page-number" href="/page/7/">7</a><a class="extend next" rel="next" href="/page/7/">下一页 &raquo;</a>
    </nav>
</section>
            
                
<aside id="sidebar">
   
        
    <div class="widget-wrap">
        <h3 class="widget-title">最新文章</h3>
        <div class="widget">
            <ul id="recent-post" class="no-thumbnail">
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/IDE/">IDE</a></p>
                            <p class="item-title"><a href="/2018/04/20/项目相关的CVS操作/" class="title">项目相关的CVS操作</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.467Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/J2EE/">J2EE</a></p>
                            <p class="item-title"><a href="/2018/04/20/图片转换PDF文件/" class="title">图片转换PDF文件</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.449Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/Spring/">Spring</a></p>
                            <p class="item-title"><a href="/2018/04/20/使用 Spring Data JPA 简化 JPA 开发/" class="title">使用 Spring Data JPA 简化 JPA 开发</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.448Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2018/04/20/通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法/" class="title">通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.448Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2018/04/20/面试感悟----一名3年工作经验的程序员应该具备的技能/" class="title">面试感悟----一名3年工作经验的程序员应该具备的技能</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.447Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
            </ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">分类</h3>
        <div class="widget">
            <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/ActiveMQ/">ActiveMQ</a><span class="category-list-count">7</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/ExtJs/">ExtJs</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Git/">Git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/IDE/">IDE</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/J2EE/">J2EE</a><span class="category-list-count">7</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/J2EE/Spring/">Spring</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/Linux/">Linux</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Redis/">Redis</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Spring/">Spring</a><span class="category-list-count">21</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/cnn图片数据处理、显示/">cnn图片数据处理、显示</a><span class="category-list-count">1</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/cnn图片数据处理、显示/python数据分析/">python数据分析</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/web前端/">web前端</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/数据库/">数据库</a><span class="category-list-count">3</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/数据库/集群/">集群</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/集群/">集群</a><span class="category-list-count">2</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">归档</h3>
        <div class="widget">
            <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/04/">四月 2018</a><span class="archive-list-count">65</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签</h3>
        <div class="widget">
            <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apache/">Apache</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Docker/">Docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Druid/">Druid</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ImageToPDF/">ImageToPDF</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Logstash/">Logstash</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring-Boot/">Spring Boot</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/activemq/">activemq</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ajax/">ajax</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/angular/">angular</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cas/">cas</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/exception/">exception</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ext/">ext</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/extjs/">extjs</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/find/">find</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/github/">github</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ide/">ide</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/idea/">idea</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/">java</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javascript/">javascript</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jboss/">jboss</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jmx/">jmx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jpa/">jpa</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/lombok/">lombok</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mvc/">mvc</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/">mysql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/numpy/">numpy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/seo/">seo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/server/">server</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/shiro/">shiro</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a><span class="tag-list-count">17</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring-mvc/">spring mvc</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring-boo/">spring-boo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sql-server/">sql server</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/windows/">windows</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/">zookeeper</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/优化/">优化</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/分布式日志/">分布式日志</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/多线程/">多线程</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/工作/">工作</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/异常/">异常</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/异常处理/">异常处理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/搜索引擎/">搜索引擎</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据分析/">数据分析</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/汉字转拼音/">汉字转拼音</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/版本控制系统/">版本控制系统</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/程序员/">程序员</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/经验/">经验</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/集群/">集群</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/面试/">面试</a><span class="tag-list-count">1</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签云</h3>
        <div class="widget tagcloud">
            <a href="/tags/Apache/" style="font-size: 10px;">Apache</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/Druid/" style="font-size: 10px;">Druid</a> <a href="/tags/ImageToPDF/" style="font-size: 10px;">ImageToPDF</a> <a href="/tags/Logstash/" style="font-size: 10px;">Logstash</a> <a href="/tags/Spring-Boot/" style="font-size: 10px;">Spring Boot</a> <a href="/tags/activemq/" style="font-size: 18px;">activemq</a> <a href="/tags/ajax/" style="font-size: 10px;">ajax</a> <a href="/tags/angular/" style="font-size: 10px;">angular</a> <a href="/tags/cas/" style="font-size: 10px;">cas</a> <a href="/tags/docker/" style="font-size: 10px;">docker</a> <a href="/tags/exception/" style="font-size: 10px;">exception</a> <a href="/tags/ext/" style="font-size: 14px;">ext</a> <a href="/tags/extjs/" style="font-size: 14px;">extjs</a> <a href="/tags/find/" style="font-size: 10px;">find</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/ide/" style="font-size: 12px;">ide</a> <a href="/tags/idea/" style="font-size: 10px;">idea</a> <a href="/tags/java/" style="font-size: 16px;">java</a> <a href="/tags/javascript/" style="font-size: 12px;">javascript</a> <a href="/tags/jboss/" style="font-size: 10px;">jboss</a> <a href="/tags/jmx/" style="font-size: 10px;">jmx</a> <a href="/tags/jpa/" style="font-size: 10px;">jpa</a> <a href="/tags/linux/" style="font-size: 14px;">linux</a> <a href="/tags/lombok/" style="font-size: 10px;">lombok</a> <a href="/tags/mvc/" style="font-size: 10px;">mvc</a> <a href="/tags/mybatis/" style="font-size: 10px;">mybatis</a> <a href="/tags/mysql/" style="font-size: 12px;">mysql</a> <a href="/tags/nginx/" style="font-size: 10px;">nginx</a> <a href="/tags/numpy/" style="font-size: 10px;">numpy</a> <a href="/tags/seo/" style="font-size: 10px;">seo</a> <a href="/tags/server/" style="font-size: 10px;">server</a> <a href="/tags/shiro/" style="font-size: 12px;">shiro</a> <a href="/tags/spring/" style="font-size: 20px;">spring</a> <a href="/tags/spring-mvc/" style="font-size: 10px;">spring mvc</a> <a href="/tags/spring-boo/" style="font-size: 10px;">spring-boo</a> <a href="/tags/sql-server/" style="font-size: 10px;">sql server</a> <a href="/tags/windows/" style="font-size: 10px;">windows</a> <a href="/tags/zookeeper/" style="font-size: 10px;">zookeeper</a> <a href="/tags/优化/" style="font-size: 12px;">优化</a> <a href="/tags/分布式日志/" style="font-size: 10px;">分布式日志</a> <a href="/tags/多线程/" style="font-size: 10px;">多线程</a> <a href="/tags/工作/" style="font-size: 10px;">工作</a> <a href="/tags/异常/" style="font-size: 10px;">异常</a> <a href="/tags/异常处理/" style="font-size: 10px;">异常处理</a> <a href="/tags/搜索引擎/" style="font-size: 12px;">搜索引擎</a> <a href="/tags/数据分析/" style="font-size: 10px;">数据分析</a> <a href="/tags/汉字转拼音/" style="font-size: 10px;">汉字转拼音</a> <a href="/tags/版本控制系统/" style="font-size: 10px;">版本控制系统</a> <a href="/tags/程序员/" style="font-size: 10px;">程序员</a> <a href="/tags/经验/" style="font-size: 10px;">经验</a> <a href="/tags/集群/" style="font-size: 10px;">集群</a> <a href="/tags/面试/" style="font-size: 10px;">面试</a>
        </div>
    </div>

    
        
    <div class="widget-wrap widget-list">
        <h3 class="widget-title">链接</h3>
        <div class="widget">
            <ul>
                
                    <li>
                        <a href="http://www.baidu.com">百度</a>
                    </li>
                
            </ul>
        </div>
    </div>


    
    <div id="toTop" class="fa fa-angle-up"></div>
</aside>

            
        </div>
        <!--google 底部广告 -->
<div style="width:100%;text-align: center;clear: both;margin-bottom: 20px;" class="hidden-xs">
    <script async src="http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <!-- 博客footer上方的广告 -->
    <ins class="adsbygoogle"
         style="display:block"
         data-ad-client="ca-pub-8254913025324557"
         data-ad-slot="3369473724"
         data-ad-format="auto"></ins>
    <script>
        (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<footer id="footer">
    <div class="outer">
        <div id="footer-info" class="inner">
            &copy;2018 - 2018&nbsp; LErry Li &nbsp;版权所有<br>
			<a href="http://www.miitbeian.gov.cn">沪ICP备17054498号-3</a>
        </div>
    </div>
</footer>
        
    
    <script id="cy_cmt_num" src="https://changyan.sohu.com/upload/plugins/plugins.list.count.js?clientId=cytxPSIHr"></script>
    <script charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/changyan.js" ></script>
    <script type="text/javascript">
    window.changyan.api.config({
    appid: 'cytxPSIHr',
    conf: 'prod_3c92d05d8ba4377e48c5fe27c2159761'
    });
    </script>




    
        <script src="/libs/lightgallery/js/lightgallery.min.js"></script>
        <script src="/libs/lightgallery/js/lg-thumbnail.min.js"></script>
        <script src="/libs/lightgallery/js/lg-pager.min.js"></script>
        <script src="/libs/lightgallery/js/lg-autoplay.min.js"></script>
        <script src="/libs/lightgallery/js/lg-fullscreen.min.js"></script>
        <script src="/libs/lightgallery/js/lg-zoom.min.js"></script>
        <script src="/libs/lightgallery/js/lg-hash.min.js"></script>
        <script src="/libs/lightgallery/js/lg-share.min.js"></script>
        <script src="/libs/lightgallery/js/lg-video.min.js"></script>
    
    
        <script src="/libs/justified-gallery/jquery.justifiedGallery.min.js"></script>
    
    



<!-- Custom Scripts -->
<script src="/js/main.js"></script>

    </div>
</body>
</html>